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

difftreelog

refactor revert changing collection owner type

Yaroslav Bolyukin2021-06-02parent: #1824786.patch.diff
in: master
Not necessary yet, we don't have to support evm accounts create
collections

3 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16    construct_runtime, decl_event, decl_module, decl_storage, decl_error,17    dispatch::DispatchResult,18    ensure, fail, parameter_types,19    traits::{20        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21        Randomness, IsSubType, WithdrawReasons,22    },23    weights::{24        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26        WeightToFeePolynomial, DispatchClass,27    },28    StorageValue,29    transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_ethereum::EthereumTransactionSender;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::NftErcSupport;59pub use eth::account::*;6061pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;62pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;63pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;64pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6566// Structs67// #region6869pub type CollectionId = u32;70pub type TokenId = u32;71pub type DecimalPoints = u8;7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum CollectionMode {76    Invalid,77    NFT,78    // decimal points79    Fungible(DecimalPoints),80    ReFungible,81}8283impl Default for CollectionMode {84    fn default() -> Self {85        Self::Invalid86    }87}8889impl Into<u8> for CollectionMode {90    fn into(self) -> u8 {91        match self {92            CollectionMode::Invalid => 0,93            CollectionMode::NFT => 1,94            CollectionMode::Fungible(_) => 2,95            CollectionMode::ReFungible => 3,96        }97    }98}99100#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub enum AccessMode {103    Normal,104    WhiteList,105}106impl Default for AccessMode {107    fn default() -> Self {108        Self::Normal109    }110}111112#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]113#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]114pub enum SchemaVersion {115    ImageURL,116    Unique,117}118impl Default for SchemaVersion {119    fn default() -> Self {120        Self::ImageURL121    }122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct Ownership<AccountId> {127    pub owner: AccountId,128    pub fraction: u128,129}130131#[derive(Encode, Decode, Debug, Clone, PartialEq)]132#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]133pub enum SponsorshipState<AccountId> {134    /// The fees are applied to the transaction sender135    Disabled,136    Unconfirmed(AccountId),137    /// Transactions are sponsored by specified account138    Confirmed(AccountId),139}140141impl<AccountId> SponsorshipState<AccountId> {142    fn sponsor(&self) -> Option<&AccountId> {143        match self {144            Self::Confirmed(sponsor) => Some(sponsor),145            _ => None,146        }147    }148149    fn pending_sponsor(&self) -> Option<&AccountId> {150        match self {151            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),152            _ => None,153        }154    }155156    fn confirmed(&self) -> bool {157        matches!(self, Self::Confirmed(_))158    }159}160161impl<T> Default for SponsorshipState<T> {162    fn default() -> Self {163        Self::Disabled164    }165}166167#[derive(Encode, Decode, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct Collection<T: Config> {170    pub owner: T::CrossAccountId,171    pub mode: CollectionMode,172    pub access: AccessMode,173    pub decimal_points: DecimalPoints,174    pub name: Vec<u16>,        // 64 include null escape char175    pub description: Vec<u16>, // 256 include null escape char176    pub token_prefix: Vec<u8>, // 16 include null escape char177    pub mint_mode: bool,178    pub offchain_schema: Vec<u8>,179    pub schema_version: SchemaVersion,180    pub sponsorship: SponsorshipState<T::AccountId>,181    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 182    pub variable_on_chain_schema: Vec<u8>, //183    pub const_on_chain_schema: Vec<u8>, //184}185186pub struct CollectionHandle<T: Config> {187    pub id: CollectionId,188    collection: Collection<T>,189    logs: eth::log::LogRecorder,190}191impl<T: Config> CollectionHandle<T> {192	pub fn get(id: CollectionId) -> Option<Self> {193		<CollectionById<T>>::get(id)194			.map(|collection| Self {195				id,196				collection,197                logs: eth::log::LogRecorder::default(),198			})199	}200    pub fn log(&self, topics: Vec<H256>, data: eth::abi::AbiWriter) {201        self.logs.log(topics, data)202    }203    pub fn into_inner(self) -> Collection<T> {204        self.collection.clone()205    }206}207208impl<T: Config> Deref for CollectionHandle<T> {209    type Target = Collection<T>;210211    fn deref(&self) -> &Self::Target {212        &self.collection213    }214}215216impl<T: Config> DerefMut for CollectionHandle<T> {217    fn deref_mut(&mut self) -> &mut Self::Target {218        &mut self.collection219    }220}221222#[derive(Encode, Decode, Debug, Clone, PartialEq)]223#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]224pub struct NftItemType<AccountId> {225    pub owner: AccountId,226    pub const_data: Vec<u8>,227    pub variable_data: Vec<u8>,228}229230#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]231#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]232pub struct FungibleItemType {233    pub value: u128,234}235236#[derive(Encode, Decode, Debug, Clone, PartialEq)]237#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]238pub struct ReFungibleItemType<AccountId> {239    pub owner: Vec<Ownership<AccountId>>,240    pub const_data: Vec<u8>,241    pub variable_data: Vec<u8>,242}243244// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246// pub struct VestingItem<AccountId, Moment> {247//     pub sender: AccountId,248//     pub recipient: AccountId,249//     pub collection_id: CollectionId,250//     pub item_id: TokenId,251//     pub amount: u64,252//     pub vesting_date: Moment,253// }254255#[derive(Encode, Decode, Debug, Clone, PartialEq)]256#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]257pub struct CollectionLimits<BlockNumber: Encode + Decode> {258    pub account_token_ownership_limit: u32,259    pub sponsored_data_size: u32,260    /// None - setVariableMetadata is not sponsored261    /// Some(v) - setVariableMetadata is sponsored 262    ///           if there is v block between txs263    pub sponsored_data_rate_limit: Option<BlockNumber>,264    pub token_limit: u32,265266    // Timeouts for item types in passed blocks267    pub sponsor_transfer_timeout: u32,268    pub owner_can_transfer: bool,269    pub owner_can_destroy: bool,270}271272impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {273    fn default() -> Self {274        Self { 275            account_token_ownership_limit: 10_000_000, 276            token_limit: u32::max_value(),277            sponsored_data_size: u32::MAX, 278            sponsored_data_rate_limit: None,279            sponsor_transfer_timeout: 14400,280            owner_can_transfer: true,281            owner_can_destroy: true282        }283    }284}285286#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]287#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]288pub struct ChainLimits {289    pub collection_numbers_limit: u32,290    pub account_token_ownership_limit: u32,291    pub collections_admins_limit: u64,292    pub custom_data_limit: u32,293294    // Timeouts for item types in passed blocks295    pub nft_sponsor_transfer_timeout: u32,296    pub fungible_sponsor_transfer_timeout: u32,297    pub refungible_sponsor_transfer_timeout: u32,298299    // Schema limits300    pub offchain_schema_limit: u32,301    pub variable_on_chain_schema_limit: u32,302    pub const_on_chain_schema_limit: u32,303}304305pub trait WeightInfo {306	fn create_collection() -> Weight;307	fn destroy_collection() -> Weight;308	fn add_to_white_list() -> Weight;309	fn remove_from_white_list() -> Weight;310    fn set_public_access_mode() -> Weight;311    fn set_mint_permission() -> Weight;312    fn change_collection_owner() -> Weight;313    fn add_collection_admin() -> Weight;314    fn remove_collection_admin() -> Weight;315    fn set_collection_sponsor() -> Weight;316    fn confirm_sponsorship() -> Weight;317    fn remove_collection_sponsor() -> Weight;318    fn create_item(s: usize) -> Weight;319    fn burn_item() -> Weight;320    fn transfer() -> Weight;321    fn approve() -> Weight;322    fn transfer_from() -> Weight;323    fn set_offchain_schema() -> Weight;324    fn set_const_on_chain_schema() -> Weight;325    fn set_variable_on_chain_schema() -> Weight;326    fn set_variable_meta_data() -> Weight;327    fn enable_contract_sponsoring() -> Weight;328    fn set_schema_version() -> Weight;329    fn set_chain_limits() -> Weight;330    fn set_contract_sponsoring_rate_limit() -> Weight;331    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;332    fn toggle_contract_white_list() -> Weight;333    fn add_to_contract_white_list() -> Weight;334    fn remove_from_contract_white_list() -> Weight;335    fn set_collection_limits() -> Weight;336}337338#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]339#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]340pub struct CreateNftData {341    pub const_data: Vec<u8>,342    pub variable_data: Vec<u8>,343}344345#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]346#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]347pub struct CreateFungibleData {348    pub value: u128,349}350351#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]352#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]353pub struct CreateReFungibleData {354    pub const_data: Vec<u8>,355    pub variable_data: Vec<u8>,356    pub pieces: u128,357}358359#[derive(Encode, Decode, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub enum CreateItemData {362    NFT(CreateNftData),363    Fungible(CreateFungibleData),364    ReFungible(CreateReFungibleData),365}366367impl CreateItemData {368    pub fn len(&self) -> usize {369        let len = match self {370            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),371            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),372            _ => 0373        };374        375        return len;376    }377}378379impl From<CreateNftData> for CreateItemData {380    fn from(item: CreateNftData) -> Self {381        CreateItemData::NFT(item)382    }383}384385impl From<CreateReFungibleData> for CreateItemData {386    fn from(item: CreateReFungibleData) -> Self {387        CreateItemData::ReFungible(item)388    }389}390391impl From<CreateFungibleData> for CreateItemData {392    fn from(item: CreateFungibleData) -> Self {393        CreateItemData::Fungible(item)394    }395}396397398decl_error! {399	/// Error for non-fungible-token module.400	pub enum Error for Module<T: Config> {401        /// Total collections bound exceeded.402        TotalCollectionsLimitExceeded,403		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.404        CollectionDecimalPointLimitExceeded, 405        /// Collection name can not be longer than 63 char.406        CollectionNameLimitExceeded, 407        /// Collection description can not be longer than 255 char.408        CollectionDescriptionLimitExceeded, 409        /// Token prefix can not be longer than 15 char.410        CollectionTokenPrefixLimitExceeded,411        /// This collection does not exist.412        CollectionNotFound,413        /// Item not exists.414        TokenNotFound,415        /// Admin not found416        AdminNotFound,417        /// Arithmetic calculation overflow.418        NumOverflow,       419        /// Account already has admin role.420        AlreadyAdmin,  421        /// You do not own this collection.422        NoPermission,423        /// This address is not set as sponsor, use setCollectionSponsor first.424        ConfirmUnsetSponsorFail,425        /// Collection is not in mint mode.426        PublicMintingNotAllowed,427        /// Sender parameter and item owner must be equal.428        MustBeTokenOwner,429        /// Item balance not enough.430        TokenValueTooLow,431        /// Size of item is too large.432        NftSizeLimitExceeded,433        /// No approve found434        ApproveNotFound,435        /// Requested value more than approved.436        TokenValueNotEnough,437        /// Only approved addresses can call this method.438        ApproveRequired,439        /// Address is not in white list.440        AddresNotInWhiteList,441        /// Number of collection admins bound exceeded.442        CollectionAdminsLimitExceeded,443        /// Owned tokens by a single address bound exceeded.444        AddressOwnershipLimitExceeded,445        /// Length of items properties must be greater than 0.446        EmptyArgument,447        /// const_data exceeded data limit.448        TokenConstDataLimitExceeded,449        /// variable_data exceeded data limit.450        TokenVariableDataLimitExceeded,451        /// Not NFT item data used to mint in NFT collection.452        NotNftDataUsedToMintNftCollectionToken,453        /// Not Fungible item data used to mint in Fungible collection.454        NotFungibleDataUsedToMintFungibleCollectionToken,455        /// Not Re Fungible item data used to mint in Re Fungible collection.456        NotReFungibleDataUsedToMintReFungibleCollectionToken,457        /// Unexpected collection type.458        UnexpectedCollectionType,459        /// Can't store metadata in fungible tokens.460        CantStoreMetadataInFungibleTokens,461        /// Collection token limit exceeded462        CollectionTokenLimitExceeded,463        /// Account token limit exceeded per collection464        AccountTokenLimitExceeded,465        /// Collection limit bounds per collection exceeded466        CollectionLimitBoundsExceeded,467        /// Tried to enable permissions which are only permitted to be disabled468        OwnerPermissionsCantBeReverted,469        /// Schema data size limit bound exceeded470        SchemaDataLimitExceeded,471        /// Maximum refungibility exceeded472        WrongRefungiblePieces,473        /// createRefungible should be called with one owner474        BadCreateRefungibleCall,475	}476}477478pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {479    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;480481    /// Weight information for extrinsics in this pallet.482	type WeightInfo: WeightInfo;483484    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;485    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;486    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;487488	type CrossAccountId: CrossAccountId<Self::AccountId>;489    type Currency: Currency<Self::AccountId>;490    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;491    type TreasuryAccountId: Get<Self::AccountId>;492493    type EthereumChainId: Get<u64>;494    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;495}496497#[cfg(feature = "runtime-benchmarks")]498mod benchmarking;499500// #endregion501502// # Used definitions503//504// ## User control levels505//506// chain-controlled - key is uncontrolled by user507//                    i.e autoincrementing index508//                    can use non-cryptographic hash509// real - key is controlled by user510//        but it is hard to generate enough colliding values, i.e owner of signed txs511//        can use non-cryptographic hash512// controlled - key is completly controlled by users513//              i.e maps with mutable keys514//              should use cryptographic hash515//516// ## User control level downgrade reasons517//518// ?1 - chain-controlled -> controlled519//      collections/tokens can be destroyed, resulting in massive holes520// ?2 - chain-controlled -> controlled521//      same as ?1, but can be only added, resulting in easier exploitation522// ?3 - real -> controlled523//      no confirmation required, so addresses can be easily generated524decl_storage! {525    trait Store for Module<T: Config> as Nft {526527        //#region Private members528        /// Id of next collection529        CreatedCollectionCount: u32;530        /// Used for migrations531        ChainVersion: u64;532        /// Id of last collection token533        /// Collection id (controlled?1)534        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;535        //#endregion536537        //#region Chain limits struct538        pub ChainLimit get(fn chain_limit) config(): ChainLimits;539        //#endregion540541        //#region Bound counters542        /// Amount of collections destroyed, used for total amount tracking with543        /// CreatedCollectionCount544        DestroyedCollectionCount: u32;545        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)546        /// Account id (real)547        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;548        //#endregion549550        //#region Basic collections551        /// Collection info552        /// Collection id (controlled?1)553        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;554        /// List of collection admins555        /// Collection id (controlled?2)556        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;557        /// Whitelisted collection users558        /// Collection id (controlled?2), user id (controlled?3)559        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;560        //#endregion561562        /// How many of collection items user have563        /// Collection id (controlled?2), account id (real)564        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;565566        /// Amount of items which spender can transfer out of owners account (via transferFrom)567        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))568        /// TODO: Off chain worker should remove from this map when token gets removed569        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;570571        //#region Item collections572        /// Collection id (controlled?2), token id (controlled?1)573        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;574        /// Collection id (controlled?2), owner (controlled?2)575        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;576        /// Collection id (controlled?2), token id (controlled?1)577        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;578        //#endregion579580        //#region Index list581        /// Collection id (controlled?2), tokens owner (controlled?2)582        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;583        //#endregion584585        //#region Tokens transfer rate limit baskets586        /// (Collection id (controlled?2), who created (real))587        /// TODO: Off chain worker should remove from this map when collection gets removed588        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;589        /// Collection id (controlled?2), token id (controlled?2)590        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;591        /// Collection id (controlled?2), owning user (real)592        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;593        /// Collection id (controlled?2), token id (controlled?2)594        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;595        //#endregion596597        /// Variable metadata sponsoring598        /// Collection id (controlled?2), token id (controlled?2)599        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;600      601        //#region Contract Sponsorship and Ownership602        /// Contract address (real)603        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;604        /// Contract address (real)605        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;606        /// (Contract address(real), caller (real))607        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;608        /// Contract address (real)609        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;610        /// Contract address (real)611        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 612        /// Contract address (real) => Whitelisted user (controlled?3)613        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 614        //#endregion615    }616    add_extra_genesis {617        build(|config: &GenesisConfig<T>| {618            // Modification of storage619            for (_num, _c) in &config.collection_id {620                <Module<T>>::init_collection(_c);621            }622623            for (_num, _c, _i) in &config.nft_item_id {624                <Module<T>>::init_nft_token(*_c, _i);625            }626627            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {628                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);629            }630631            for (_num, _c, _i) in &config.refungible_item_id {632                <Module<T>>::init_refungible_token(*_c, _i);633            }634        })635    }636}637638decl_event!(639    pub enum Event<T>640    where641        CrossAccountId = <T as Config>::CrossAccountId,642    {643        /// New collection was created644        /// 645        /// # Arguments646        /// 647        /// * collection_id: Globally unique identifier of newly created collection.648        /// 649        /// * mode: [CollectionMode] converted into u8.650        /// 651        /// * account_id: Collection owner.652        CollectionCreated(CollectionId, u8, CrossAccountId),653654        /// New item was created.655        /// 656        /// # Arguments657        /// 658        /// * collection_id: Id of the collection where item was created.659        /// 660        /// * item_id: Id of an item. Unique within the collection.661        ///662        /// * recipient: Owner of newly created item 663        ItemCreated(CollectionId, TokenId, CrossAccountId),664665        /// Collection item was burned.666        /// 667        /// # Arguments668        /// 669        /// collection_id.670        /// 671        /// item_id: Identifier of burned NFT.672        ItemDestroyed(CollectionId, TokenId),673674        /// Item was transferred675        ///676        /// * collection_id: Id of collection to which item is belong677        ///678        /// * item_id: Id of an item679        ///680        /// * sender: Original owner of item681        ///682        /// * recipient: New owner of item683        ///684        /// * amount: Always 1 for NFT685        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),686687        /// * collection_id688        ///689        /// * item_id690        ///691        /// * sender692        ///693        /// * spender694        ///695        /// * amount696        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),697    }698);699700decl_module! {701    pub struct Module<T: Config> for enum Call 702    where 703        origin: T::Origin704    {705        fn deposit_event() = default;706        type Error = Error<T>;707708        fn on_initialize(now: T::BlockNumber) -> Weight {709            0710        }711712        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.713        /// 714        /// # Permissions715        /// 716        /// * Anyone.717        /// 718        /// # Arguments719        /// 720        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.721        /// 722        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.723        /// 724        /// * token_prefix: UTF-8 string with token prefix.725        /// 726        /// * mode: [CollectionMode] collection type and type dependent data.727        // returns collection ID728        #[weight = <T as Config>::WeightInfo::create_collection()]729        #[transactional]730        pub fn create_collection(origin,731                                 collection_name: Vec<u16>,732                                 collection_description: Vec<u16>,733                                 token_prefix: Vec<u8>,734                                 mode: CollectionMode) -> DispatchResult {735736            // Anyone can create a collection737            let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);738739            // Take a (non-refundable) deposit of collection creation740            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();741            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(742                &T::TreasuryAccountId::get(),743                T::CollectionCreationPrice::get(),744            ));745            <T as Config>::Currency::settle(746                who.as_sub(),747                imbalance,748                WithdrawReasons::TRANSFER,749                ExistenceRequirement::KeepAlive,750            ).map_err(|_| Error::<T>::NoPermission)?;751752            let decimal_points = match mode {753                CollectionMode::Fungible(points) => points,754                _ => 0755            };756757            let chain_limit = ChainLimit::get();758759            let created_count = CreatedCollectionCount::get();760            let destroyed_count = DestroyedCollectionCount::get();761762            // bound Total number of collections763            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);764765            // check params766            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);767            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);768            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);769            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);770771            // Generate next collection ID772            let next_id = created_count773                .checked_add(1)774                .ok_or(Error::<T>::NumOverflow)?;775776            CreatedCollectionCount::put(next_id);777778            let limits = CollectionLimits {779                sponsored_data_size: chain_limit.custom_data_limit,780                ..Default::default()781            };782783            // Create new collection784            let new_collection = Collection {785                owner: who.clone(),786                name: collection_name,787                mode: mode.clone(),788                mint_mode: false,789                access: AccessMode::Normal,790                description: collection_description,791                decimal_points: decimal_points,792                token_prefix: token_prefix,793                offchain_schema: Vec::new(),794                schema_version: SchemaVersion::ImageURL,795                sponsorship: SponsorshipState::Disabled,796                variable_on_chain_schema: Vec::new(),797                const_on_chain_schema: Vec::new(),798                limits,799            };800801            // Add new collection to map802            <CollectionById<T>>::insert(next_id, new_collection);803804            // call event805            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));806807            Ok(())808        }809810        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.811        /// 812        /// # Permissions813        /// 814        /// * Collection Owner.815        /// 816        /// # Arguments817        /// 818        /// * collection_id: collection to destroy.819        #[weight = <T as Config>::WeightInfo::destroy_collection()]820        #[transactional]821        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {822823            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);824            let collection = Self::get_collection(collection_id)?;825            Self::check_owner_permissions(&collection, &sender)?;826            if !collection.limits.owner_can_destroy {827                fail!(Error::<T>::NoPermission);828            }829830            <AddressTokens<T>>::remove_prefix(collection_id);831            <Allowances<T>>::remove_prefix(collection_id);832            <Balance<T>>::remove_prefix(collection_id);833            <ItemListIndex>::remove(collection_id);834            <AdminList<T>>::remove(collection_id);835            <CollectionById<T>>::remove(collection_id);836            <WhiteList<T>>::remove_prefix(collection_id);837838            <NftItemList<T>>::remove_prefix(collection_id);839            <FungibleItemList<T>>::remove_prefix(collection_id);840            <ReFungibleItemList<T>>::remove_prefix(collection_id);841842            <NftTransferBasket<T>>::remove_prefix(collection_id);843            <FungibleTransferBasket<T>>::remove_prefix(collection_id);844            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);845846            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);847848            DestroyedCollectionCount::put(DestroyedCollectionCount::get()849                .checked_add(1)850                .ok_or(Error::<T>::NumOverflow)?);851852            Ok(())853        }854855        /// Add an address to white list.856        /// 857        /// # Permissions858        /// 859        /// * Collection Owner860        /// * Collection Admin861        /// 862        /// # Arguments863        /// 864        /// * collection_id.865        /// 866        /// * address.867        #[weight = <T as Config>::WeightInfo::add_to_white_list()]868        #[transactional]869        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{870871            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);872            let collection = Self::get_collection(collection_id)?;873874            Self::toggle_white_list_internal(875                &sender,876                &collection,877                &address,878                true,879            )?;880881            Ok(())882        }883884        /// Remove an address from white list.885        /// 886        /// # Permissions887        /// 888        /// * Collection Owner889        /// * Collection Admin890        /// 891        /// # Arguments892        /// 893        /// * collection_id.894        /// 895        /// * address.896        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]897        #[transactional]898        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{899900            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);901            let collection = Self::get_collection(collection_id)?;902903            Self::toggle_white_list_internal(904                &sender,905                &collection,906                &address,907                false,908            )?;909910            Ok(())911        }912913        /// Toggle between normal and white list access for the methods with access for `Anyone`.914        /// 915        /// # Permissions916        /// 917        /// * Collection Owner.918        /// 919        /// # Arguments920        /// 921        /// * collection_id.922        /// 923        /// * mode: [AccessMode]924        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]925        #[transactional]926        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult927        {928            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);929930            let mut target_collection = Self::get_collection(collection_id)?;931            Self::check_owner_permissions(&target_collection, &sender)?;932            target_collection.access = mode;933            Self::save_collection(target_collection);934935            Ok(())936        }937938        /// Allows Anyone to create tokens if:939        /// * White List is enabled, and940        /// * Address is added to white list, and941        /// * This method was called with True parameter942        /// 943        /// # Permissions944        /// * Collection Owner945        ///946        /// # Arguments947        /// 948        /// * collection_id.949        /// 950        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.951        #[weight = <T as Config>::WeightInfo::set_mint_permission()]952        #[transactional]953        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult954        {955            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);956957            let mut target_collection = Self::get_collection(collection_id)?;958            Self::check_owner_permissions(&target_collection, &sender)?;959            target_collection.mint_mode = mint_permission;960            Self::save_collection(target_collection);961962            Ok(())963        }964965        /// Change the owner of the collection.966        /// 967        /// # Permissions968        /// 969        /// * Collection Owner.970        /// 971        /// # Arguments972        /// 973        /// * collection_id.974        /// 975        /// * new_owner.976        #[weight = <T as Config>::WeightInfo::change_collection_owner()]977        #[transactional]978        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {979980            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981            let mut target_collection = Self::get_collection(collection_id)?;982            Self::check_owner_permissions(&target_collection, &sender)?;983            target_collection.owner = new_owner;984            Self::save_collection(target_collection);985986            Ok(())987        }988989        /// Adds an admin of the Collection.990        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 991        /// 992        /// # Permissions993        /// 994        /// * Collection Owner.995        /// * Collection Admin.996        /// 997        /// # Arguments998        /// 999        /// * collection_id: ID of the Collection to add admin for.1000        /// 1001        /// * new_admin_id: Address of new admin to add.1002        #[weight = <T as Config>::WeightInfo::add_collection_admin()]1003        #[transactional]1004        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1005            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006            let collection = Self::get_collection(collection_id)?;1007            Self::check_owner_or_admin_permissions(&collection, &sender)?;1008            let mut admin_arr = <AdminList<T>>::get(collection_id);10091010            match admin_arr.binary_search(&new_admin_id) {1011                Ok(_) => {},1012                Err(idx) => {1013                    let limits = ChainLimit::get();1014                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1015                    admin_arr.insert(idx, new_admin_id);1016                    <AdminList<T>>::insert(collection_id, admin_arr);1017                }1018            }1019            Ok(())1020        }10211022        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.1023        ///1024        /// # Permissions1025        /// 1026        /// * Collection Owner.1027        /// * Collection Admin.1028        /// 1029        /// # Arguments1030        /// 1031        /// * collection_id: ID of the Collection to remove admin for.1032        /// 1033        /// * account_id: Address of admin to remove.1034        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1035        #[transactional]1036        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1037            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038            let collection = Self::get_collection(collection_id)?;1039            Self::check_owner_or_admin_permissions(&collection, &sender)?;1040            let mut admin_arr = <AdminList<T>>::get(collection_id);10411042            match admin_arr.binary_search(&account_id) {1043                Ok(idx) => {1044                    admin_arr.remove(idx);1045                    <AdminList<T>>::insert(collection_id, admin_arr);1046                },1047                Err(_) => {}1048            }1049            Ok(())1050        }10511052        /// # Permissions1053        /// 1054        /// * Collection Owner1055        /// 1056        /// # Arguments1057        /// 1058        /// * collection_id.1059        /// 1060        /// * new_sponsor.1061        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1062        #[transactional]1063        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1064            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1065            let mut target_collection = Self::get_collection(collection_id)?;1066            Self::check_owner_permissions(&target_collection, &sender)?;10671068            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1069            Self::save_collection(target_collection);10701071            Ok(())1072        }10731074        /// # Permissions1075        /// 1076        /// * Sponsor.1077        /// 1078        /// # Arguments1079        /// 1080        /// * collection_id.1081        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1082        #[transactional]1083        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1084            let sender = ensure_signed(origin)?;10851086            let mut target_collection = Self::get_collection(collection_id)?;1087            ensure!(1088                target_collection.sponsorship.pending_sponsor() == Some(&sender),1089                Error::<T>::ConfirmUnsetSponsorFail1090            );10911092            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1093            Self::save_collection(target_collection);10941095            Ok(())1096        }10971098        /// Switch back to pay-per-own-transaction model.1099        ///1100        /// # Permissions1101        ///1102        /// * Collection owner.1103        /// 1104        /// # Arguments1105        /// 1106        /// * collection_id.1107        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1108        #[transactional]1109        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1110            let sender = ensure_signed(origin)?;11111112            let mut target_collection = Self::get_collection(collection_id)?;1113            Self::check_owner_permissions(&target_collection, &T::CrossAccountId::from_sub(sender))?;11141115            target_collection.sponsorship = SponsorshipState::Disabled;1116            Self::save_collection(target_collection);11171118            Ok(())1119        }11201121        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1122        /// 1123        /// # Permissions1124        /// 1125        /// * Collection Owner.1126        /// * Collection Admin.1127        /// * Anyone if1128        ///     * White List is enabled, and1129        ///     * Address is added to white list, and1130        ///     * MintPermission is enabled (see SetMintPermission method)1131        /// 1132        /// # Arguments1133        /// 1134        /// * collection_id: ID of the collection.1135        /// 1136        /// * owner: Address, initial owner of the NFT.1137        ///1138        /// * data: Token data to store on chain.1139        // #[weight =1140        // (130_000_000 as Weight)1141        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1142        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1143        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11441145        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1146        #[transactional]1147        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1148            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1149            let collection = Self::get_collection(collection_id)?;11501151            Self::create_item_internal(&sender, &collection, &owner, data)?;11521153            Self::submit_logs(collection)?;1154            Ok(())1155        }11561157        /// This method creates multiple items in a collection created with CreateCollection method.1158        /// 1159        /// # Permissions1160        /// 1161        /// * Collection Owner.1162        /// * Collection Admin.1163        /// * Anyone if1164        ///     * White List is enabled, and1165        ///     * Address is added to white list, and1166        ///     * MintPermission is enabled (see SetMintPermission method)1167        /// 1168        /// # Arguments1169        /// 1170        /// * collection_id: ID of the collection.1171        /// 1172        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1173        /// 1174        /// * owner: Address, initial owner of the NFT.1175        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1176                               .map(|data| { data.len() })1177                               .sum())]1178        #[transactional]1179        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11801181            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1182            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1183            let collection = Self::get_collection(collection_id)?;11841185            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11861187            Self::submit_logs(collection)?;1188            Ok(())1189        }11901191        /// Destroys a concrete instance of NFT.1192        /// 1193        /// # Permissions1194        /// 1195        /// * Collection Owner.1196        /// * Collection Admin.1197        /// * Current NFT Owner.1198        /// 1199        /// # Arguments1200        /// 1201        /// * collection_id: ID of the collection.1202        /// 1203        /// * item_id: ID of NFT to burn.1204        #[weight = <T as Config>::WeightInfo::burn_item()]1205        #[transactional]1206        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12071208            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1209            let target_collection = Self::get_collection(collection_id)?;12101211            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12121213            Self::submit_logs(target_collection)?;1214            Ok(())1215        }12161217        /// Change ownership of the token.1218        /// 1219        /// # Permissions1220        /// 1221        /// * Collection Owner1222        /// * Collection Admin1223        /// * Current NFT owner1224        ///1225        /// # Arguments1226        /// 1227        /// * recipient: Address of token recipient.1228        /// 1229        /// * collection_id.1230        /// 1231        /// * item_id: ID of the item1232        ///     * Non-Fungible Mode: Required.1233        ///     * Fungible Mode: Ignored.1234        ///     * Re-Fungible Mode: Required.1235        /// 1236        /// * value: Amount to transfer.1237        ///     * Non-Fungible Mode: Ignored1238        ///     * Fungible Mode: Must specify transferred amount1239        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1240        #[weight = <T as Config>::WeightInfo::transfer()]1241        #[transactional]1242        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1243            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1244            let collection = Self::get_collection(collection_id)?;12451246            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12471248            Self::submit_logs(collection)?;1249            Ok(())1250        }12511252        /// Set, change, or remove approved address to transfer the ownership of the NFT.1253        /// 1254        /// # Permissions1255        /// 1256        /// * Collection Owner1257        /// * Collection Admin1258        /// * Current NFT owner1259        /// 1260        /// # Arguments1261        /// 1262        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1263        /// 1264        /// * collection_id.1265        /// 1266        /// * item_id: ID of the item.1267        #[weight = <T as Config>::WeightInfo::approve()]1268        #[transactional]1269        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1270            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1271            let collection = Self::get_collection(collection_id)?;12721273            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12741275            Self::submit_logs(collection)?;1276            Ok(())1277        }1278        1279        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1280        /// 1281        /// # Permissions1282        /// * Collection Owner1283        /// * Collection Admin1284        /// * Current NFT owner1285        /// * Address approved by current NFT owner1286        /// 1287        /// # Arguments1288        /// 1289        /// * from: Address that owns token.1290        /// 1291        /// * recipient: Address of token recipient.1292        /// 1293        /// * collection_id.1294        /// 1295        /// * item_id: ID of the item.1296        /// 1297        /// * value: Amount to transfer.1298        #[weight = <T as Config>::WeightInfo::transfer_from()]1299        #[transactional]1300        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1301            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1302            let collection = Self::get_collection(collection_id)?;13031304            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13051306            Self::submit_logs(collection)?;1307            Ok(())1308        }1309        // #[weight = 0]1310        //     // let no_perm_mes = "You do not have permissions to modify this collection";1311        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1312        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1313        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13141315        //     // // on_nft_received  call13161317        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13181319        //     Ok(())1320        // }13211322        /// Set off-chain data schema.1323        /// 1324        /// # Permissions1325        /// 1326        /// * Collection Owner1327        /// * Collection Admin1328        /// 1329        /// # Arguments1330        /// 1331        /// * collection_id.1332        /// 1333        /// * schema: String representing the offchain data schema.1334        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1335        #[transactional]1336        pub fn set_variable_meta_data (1337            origin,1338            collection_id: CollectionId,1339            item_id: TokenId,1340            data: Vec<u8>1341        ) -> DispatchResult {1342            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1343            1344            let collection = Self::get_collection(collection_id)?;13451346            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13471348            Ok(())1349        }1350 1351        /// Set schema standard1352        /// ImageURL1353        /// Unique1354        /// 1355        /// # Permissions1356        /// 1357        /// * Collection Owner1358        /// * Collection Admin1359        /// 1360        /// # Arguments1361        /// 1362        /// * collection_id.1363        /// 1364        /// * schema: SchemaVersion: enum1365        #[weight = <T as Config>::WeightInfo::set_schema_version()]1366        #[transactional]1367        pub fn set_schema_version(1368            origin,1369            collection_id: CollectionId,1370            version: SchemaVersion1371        ) -> DispatchResult {1372            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1373            let mut target_collection = Self::get_collection(collection_id)?;1374            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1375            target_collection.schema_version = version;1376            Self::save_collection(target_collection);13771378            Ok(())1379        }13801381        /// Set off-chain data schema.1382        /// 1383        /// # Permissions1384        /// 1385        /// * Collection Owner1386        /// * Collection Admin1387        /// 1388        /// # Arguments1389        /// 1390        /// * collection_id.1391        /// 1392        /// * schema: String representing the offchain data schema.1393        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1394        #[transactional]1395        pub fn set_offchain_schema(1396            origin,1397            collection_id: CollectionId,1398            schema: Vec<u8>1399        ) -> DispatchResult {1400            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1401            let mut target_collection = Self::get_collection(collection_id)?;1402            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14031404            // check schema limit1405            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14061407            target_collection.offchain_schema = schema;1408            Self::save_collection(target_collection);14091410            Ok(())1411        }14121413        /// Set const on-chain data schema.1414        /// 1415        /// # Permissions1416        /// 1417        /// * Collection Owner1418        /// * Collection Admin1419        /// 1420        /// # Arguments1421        /// 1422        /// * collection_id.1423        /// 1424        /// * schema: String representing the const on-chain data schema.1425        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1426        #[transactional]1427        pub fn set_const_on_chain_schema (1428            origin,1429            collection_id: CollectionId,1430            schema: Vec<u8>1431        ) -> DispatchResult {1432            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1433            let mut target_collection = Self::get_collection(collection_id)?;1434            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14351436            // check schema limit1437            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14381439            target_collection.const_on_chain_schema = schema;1440            Self::save_collection(target_collection);14411442            Ok(())1443        }14441445        /// Set variable on-chain data schema.1446        /// 1447        /// # Permissions1448        /// 1449        /// * Collection Owner1450        /// * Collection Admin1451        /// 1452        /// # Arguments1453        /// 1454        /// * collection_id.1455        /// 1456        /// * schema: String representing the variable on-chain data schema.1457        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1458        #[transactional]1459        pub fn set_variable_on_chain_schema (1460            origin,1461            collection_id: CollectionId,1462            schema: Vec<u8>1463        ) -> DispatchResult {1464            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1465            let mut target_collection = Self::get_collection(collection_id)?;1466            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14671468            // check schema limit1469            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14701471            target_collection.variable_on_chain_schema = schema;1472            Self::save_collection(target_collection);14731474            Ok(())1475        }14761477        // Sudo permissions function1478        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1479        #[transactional]1480        pub fn set_chain_limits(1481            origin,1482            limits: ChainLimits1483        ) -> DispatchResult {14841485            #[cfg(not(feature = "runtime-benchmarks"))]1486            ensure_root(origin)?;14871488            <ChainLimit>::put(limits);1489            Ok(())1490        }14911492        /// Enable smart contract self-sponsoring.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Contract Owner1497        /// 1498        /// # Arguments1499        /// 1500        /// * contract address1501        /// * enable flag1502        /// 1503        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1504        #[transactional]1505        pub fn enable_contract_sponsoring(1506            origin,1507            contract_address: T::AccountId,1508            enable: bool1509        ) -> DispatchResult {15101511            let sender = ensure_signed(origin)?;15121513            #[cfg(feature = "runtime-benchmarks")]1514            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15151516            Self::ensure_contract_owned(sender, &contract_address)?;15171518            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1519            Ok(())1520        }15211522        /// Set the rate limit for contract sponsoring to specified number of blocks.1523        /// 1524        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1525        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1526        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1527        /// from contract endowment if there are at least B blocks between such transactions. 1528        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1529        /// 1530        /// # Permissions1531        /// 1532        /// * Contract Owner1533        /// 1534        /// # Arguments1535        /// 1536        /// -`contract_address`: Address of the contract to sponsor1537        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1538        /// 1539        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1540        #[transactional]1541        pub fn set_contract_sponsoring_rate_limit(1542            origin,1543            contract_address: T::AccountId,1544            rate_limit: T::BlockNumber1545        ) -> DispatchResult {1546            let sender = ensure_signed(origin)?;15471548            #[cfg(feature = "runtime-benchmarks")]1549            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15501551            Self::ensure_contract_owned(sender, &contract_address)?;1552            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1553            Ok(())1554        }15551556        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1557        /// 1558        /// # Permissions1559        /// 1560        /// * Address that deployed smart contract.1561        /// 1562        /// # Arguments1563        /// 1564        /// -`contract_address`: Address of the contract.1565        /// 1566        /// - `enable`: .  1567        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1568        #[transactional]1569        pub fn toggle_contract_white_list(1570            origin,1571            contract_address: T::AccountId,1572            enable: bool1573        ) -> DispatchResult {1574            let sender = ensure_signed(origin)?;15751576            #[cfg(feature = "runtime-benchmarks")]1577            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15781579            Self::ensure_contract_owned(sender, &contract_address)?;1580            if enable {1581                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1582            } else {1583                <ContractWhiteListEnabled<T>>::remove(contract_address);1584            }1585            Ok(())1586        }1587        1588        /// Add an address to smart contract white list.1589        /// 1590        /// # Permissions1591        /// 1592        /// * Address that deployed smart contract.1593        /// 1594        /// # Arguments1595        /// 1596        /// -`contract_address`: Address of the contract.1597        ///1598        /// -`account_address`: Address to add.1599        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1600        #[transactional]1601        pub fn add_to_contract_white_list(1602            origin,1603            contract_address: T::AccountId,1604            account_address: T::AccountId1605        ) -> DispatchResult {1606            let sender = ensure_signed(origin)?;16071608            #[cfg(feature = "runtime-benchmarks")]1609            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1610            1611            Self::ensure_contract_owned(sender, &contract_address)?;      1612            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1613            Ok(())1614        }16151616        /// Remove an address from smart contract white list.1617        /// 1618        /// # Permissions1619        /// 1620        /// * Address that deployed smart contract.1621        /// 1622        /// # Arguments1623        /// 1624        /// -`contract_address`: Address of the contract.1625        ///1626        /// -`account_address`: Address to remove.1627        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1628        #[transactional]1629        pub fn remove_from_contract_white_list(1630            origin,1631            contract_address: T::AccountId,1632            account_address: T::AccountId1633        ) -> DispatchResult {1634            let sender = ensure_signed(origin)?;16351636            #[cfg(feature = "runtime-benchmarks")]1637            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16381639            Self::ensure_contract_owned(sender, &contract_address)?;1640            <ContractWhiteList<T>>::remove(contract_address, account_address);1641            Ok(())1642        }16431644        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1645        #[transactional]1646        pub fn set_collection_limits(1647            origin,1648            collection_id: u32,1649            new_limits: CollectionLimits<T::BlockNumber>,1650        ) -> DispatchResult {1651            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1652            let mut target_collection = Self::get_collection(collection_id)?;1653            Self::check_owner_permissions(&target_collection, &sender)?;1654            let old_limits = &target_collection.limits;1655            let chain_limits = ChainLimit::get();16561657            // collection bounds1658            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1659                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1660                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1661                Error::<T>::CollectionLimitBoundsExceeded);16621663            // token_limit   check  prev1664            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1665            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16661667            ensure!(1668                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1669                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1670                Error::<T>::OwnerPermissionsCantBeReverted,1671            );16721673            target_collection.limits = new_limits;1674            Self::save_collection(target_collection);16751676            Ok(())1677        } 1678    }1679}16801681impl<T: Config> Module<T> {1682    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1683        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1684        Self::validate_create_item_args(&collection, &data)?;1685        Self::create_item_no_validation(&collection, owner, data)?;16861687        Ok(())1688    }16891690    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1691        // Limits check1692        Self::is_correct_transfer(target_collection, &recipient)?;16931694        // Transfer permissions check1695        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1696            Self::is_owner_or_admin_permissions(target_collection, &sender),1697            Error::<T>::NoPermission);16981699        if target_collection.access == AccessMode::WhiteList {1700            Self::check_white_list(target_collection, &sender)?;1701            Self::check_white_list(target_collection, &recipient)?;1702        }17031704        match target_collection.mode1705        {1706            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1707            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1708            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1709            _ => ()1710        };17111712        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17131714        Ok(())1715    }17161717	pub fn approve_internal(1718		sender: &T::CrossAccountId,1719		spender: &T::CrossAccountId,1720		collection: &CollectionHandle<T>,1721		item_id: TokenId,1722		amount: u1281723	) -> DispatchResult {1724		Self::token_exists(&collection, item_id)?;17251726		// Transfer permissions check1727		let bypasses_limits = collection.limits.owner_can_transfer &&1728			Self::is_owner_or_admin_permissions(1729				&collection,1730				&sender,1731			);17321733		let allowance_limit = if bypasses_limits {1734			None1735		} else if let Some(amount) = Self::owned_amount(1736			&sender,1737			&collection,1738			item_id,1739		) {1740			Some(amount)1741		} else {1742			fail!(Error::<T>::NoPermission);1743		};17441745		if collection.access == AccessMode::WhiteList {1746			Self::check_white_list(&collection, &sender)?;1747			Self::check_white_list(&collection, &spender)?;1748		}17491750		let allowance: u128 = amount1751			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1752			.ok_or(Error::<T>::NumOverflow)?;1753		if let Some(limit) = allowance_limit {1754			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1755		}1756		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17571758		if matches!(collection.mode, CollectionMode::NFT) {1759			// TODO: NFT: only one owner may exist for token in ERC7211760			collection.log(1761				Vec::from([1762					eth::APPROVAL_NFT_TOPIC,1763					eth::address_to_topic(sender.as_eth()),1764					eth::address_to_topic(spender.as_eth()),1765                    eth::u32_to_topic(item_id),1766				]),1767				abi_encode!(),1768			);1769		}17701771		if matches!(collection.mode, CollectionMode::Fungible(_)) {1772			// TODO: NFT: only one owner may exist for token in ERC201773			collection.log(1774				Vec::from([1775					eth::APPROVAL_FUNGIBLE_TOPIC,1776					eth::address_to_topic(sender.as_eth()),1777					eth::address_to_topic(spender.as_eth()),1778				]),1779				abi_encode!(uint256(allowance.into())),1780			);1781		}17821783		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1784		Ok(())1785	}17861787	pub fn transfer_from_internal(1788		sender: &T::CrossAccountId,1789		from: &T::CrossAccountId,1790		recipient: &T::CrossAccountId,1791		collection: &CollectionHandle<T>,1792		item_id: TokenId,1793		amount: u128,1794	) -> DispatchResult {1795		// Check approval1796		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17971798		// Limits check1799		Self::is_correct_transfer(&collection, &recipient)?;18001801		// Transfer permissions check1802		ensure!(1803			approval >= amount || 1804			(1805				collection.limits.owner_can_transfer &&1806				Self::is_owner_or_admin_permissions(&collection, &sender)1807			),1808			Error::<T>::NoPermission1809		);18101811		if collection.access == AccessMode::WhiteList {1812			Self::check_white_list(&collection, &sender)?;1813			Self::check_white_list(&collection, &recipient)?;1814		}18151816		// Reduce approval by transferred amount or remove if remaining approval drops to 01817		let allowance = approval.saturating_sub(amount);1818		if allowance > 0 {1819			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1820		} else {1821			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1822		}18231824		match collection.mode {1825			CollectionMode::NFT => {1826				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1827			}1828			CollectionMode::Fungible(_) => {1829				Self::transfer_fungible(&collection, amount, &from, &recipient)?1830			}1831			CollectionMode::ReFungible => {1832				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1833			}1834			_ => ()1835		};18361837		if matches!(collection.mode, CollectionMode::Fungible(_)) {1838			collection.log(1839				Vec::from([1840					eth::APPROVAL_FUNGIBLE_TOPIC,1841					eth::address_to_topic(from.as_eth()),1842					eth::address_to_topic(sender.as_eth()),1843				]),1844				abi_encode!(uint256(allowance.into())),1845			);1846		}18471848		Ok(())1849	}18501851    pub fn set_variable_meta_data_internal(1852        sender: &T::CrossAccountId,1853        collection: &CollectionHandle<T>, 1854        item_id: TokenId,1855        data: Vec<u8>,1856    ) -> DispatchResult {1857        Self::token_exists(&collection, item_id)?;18581859        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18601861        // Modify permissions check1862        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1863            Self::is_owner_or_admin_permissions(&collection, &sender),1864            Error::<T>::NoPermission);18651866        match collection.mode1867        {1868            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1869            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1870            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1871            _ => fail!(Error::<T>::UnexpectedCollectionType)1872        };18731874        Ok(())1875    }18761877    pub fn create_multiple_items_internal(1878        sender: &T::CrossAccountId,1879        collection: &CollectionHandle<T>,1880        owner: &T::CrossAccountId,1881        items_data: Vec<CreateItemData>,1882    ) -> DispatchResult {1883        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18841885        for data in &items_data {1886            Self::validate_create_item_args(&collection, data)?;1887        }1888        for data in &items_data {1889            Self::create_item_no_validation(&collection, owner, data.clone())?;1890        }18911892        Ok(())1893    }18941895    pub fn burn_item_internal(1896        sender: &T::CrossAccountId,1897        collection: &CollectionHandle<T>,1898        item_id: TokenId,1899        value: u128,1900    ) -> DispatchResult {1901        ensure!(1902            Self::is_item_owner(&sender, &collection, item_id) ||1903            (1904                collection.limits.owner_can_transfer &&1905                Self::is_owner_or_admin_permissions(&collection, &sender)1906            ),1907            Error::<T>::NoPermission1908        );19091910        if collection.access == AccessMode::WhiteList {1911            Self::check_white_list(&collection, &sender)?;1912        }19131914        match collection.mode1915        {1916            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1917            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1918            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1919            _ => ()1920        };19211922        Ok(())1923    }19241925    pub fn toggle_white_list_internal(1926        sender: &T::CrossAccountId,1927        collection: &CollectionHandle<T>,1928        address: &T::CrossAccountId,1929        whitelisted: bool,1930    ) -> DispatchResult {1931        Self::check_owner_or_admin_permissions(&collection, &sender)?;19321933        if whitelisted {1934            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1935        } else {1936            <WhiteList<T>>::remove(collection.id, address.as_sub());1937        }19381939        Ok(())1940    }19411942    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1943        let collection_id = collection.id;19441945        // check token limit and account token limit1946        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1947        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1948        1949        Ok(())1950    }19511952    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1953        let collection_id = collection.id;19541955        // check token limit and account token limit1956        let total_items: u32 = ItemListIndex::get(collection_id)1957            .checked_add(amount)1958            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1959        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1960            .checked_add(amount)1961            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1962        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1963        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19641965        if !Self::is_owner_or_admin_permissions(collection, &sender) {1966            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1967            Self::check_white_list(collection, owner)?;1968            Self::check_white_list(collection, sender)?;1969        }19701971        Ok(())1972    }19731974    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1975        match target_collection.mode1976        {1977            CollectionMode::NFT => {1978                if let CreateItemData::NFT(data) = data {1979                    // check sizes1980                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1981                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1982                } else {1983                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1984                }1985            },1986            CollectionMode::Fungible(_) => {1987                if let CreateItemData::Fungible(_) = data {1988                } else {1989                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1990                }1991            },1992            CollectionMode::ReFungible => {1993                if let CreateItemData::ReFungible(data) = data {19941995                    // check sizes1996                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1997                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19981999                    // Check refungibility limits2000                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2001                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2002                } else {2003                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2004                }2005            },2006            _ => { fail!(Error::<T>::UnexpectedCollectionType); }2007        };20082009        Ok(())2010    }20112012    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2013        match data2014        {2015            CreateItemData::NFT(data) => {2016                let item = NftItemType {2017                    owner: owner.clone(),2018                    const_data: data.const_data,2019                    variable_data: data.variable_data2020                };20212022                Self::add_nft_item(collection, item)?;2023            },2024            CreateItemData::Fungible(data) => {2025                Self::add_fungible_item(collection, &owner, data.value)?;2026            },2027            CreateItemData::ReFungible(data) => {2028                let mut owner_list = Vec::new();2029                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20302031                let item = ReFungibleItemType {2032                    owner: owner_list,2033                    const_data: data.const_data,2034                    variable_data: data.variable_data2035                };20362037                Self::add_refungible_item(collection, item)?;2038            }2039        };20402041        Ok(())2042    }20432044    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2045        let collection_id = collection.id;20462047        // Does new owner already have an account?2048        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20492050        // Mint 2051        let item = FungibleItemType {2052            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2053        };2054        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20552056        // Update balance2057        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2058            .checked_add(value)2059            .ok_or(Error::<T>::NumOverflow)?;2060        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20612062        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2063        Ok(())2064    }20652066    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2067        let collection_id = collection.id;20682069        let current_index = <ItemListIndex>::get(collection_id)2070            .checked_add(1)2071            .ok_or(Error::<T>::NumOverflow)?;2072        let itemcopy = item.clone();20732074        ensure!(2075            item.owner.len() == 1,2076            Error::<T>::BadCreateRefungibleCall,2077        );2078        let item_owner = item.owner.first().expect("only one owner is defined");20792080        let value = item_owner.fraction;2081        let owner = item_owner.owner.clone();20822083        Self::add_token_index(collection_id, current_index, &owner)?;20842085        <ItemListIndex>::insert(collection_id, current_index);2086        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20872088        // Update balance2089        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2090            .checked_add(value)2091            .ok_or(Error::<T>::NumOverflow)?;2092        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20932094        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2095        Ok(())2096    }20972098    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2099        let collection_id = collection.id;21002101        let current_index = <ItemListIndex>::get(collection_id)2102            .checked_add(1)2103            .ok_or(Error::<T>::NumOverflow)?;21042105        let item_owner = item.owner.clone();2106        Self::add_token_index(collection_id, current_index, &item.owner)?;21072108        <ItemListIndex>::insert(collection_id, current_index);2109        <NftItemList<T>>::insert(collection_id, current_index, item);21102111        // Update balance2112        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2113            .checked_add(1)2114            .ok_or(Error::<T>::NumOverflow)?;2115        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21162117        collection.log(2118            Vec::from([2119                eth::TRANSFER_NFT_TOPIC,2120                eth::address_to_topic(&H160::default()),2121                eth::address_to_topic(item_owner.as_eth()),2122                eth::u32_to_topic(current_index),2123            ]),2124            abi_encode!(),2125        );2126        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2127        Ok(())2128    }21292130    fn burn_refungible_item(2131        collection: &CollectionHandle<T>,2132        item_id: TokenId,2133        owner: &T::CrossAccountId,2134    ) -> DispatchResult {2135        let collection_id = collection.id;21362137        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2138            .ok_or(Error::<T>::TokenNotFound)?;2139        let rft_balance = token2140            .owner2141            .iter()2142            .find(|&i| i.owner == *owner)2143            .ok_or(Error::<T>::TokenNotFound)?;2144        Self::remove_token_index(collection_id, item_id, owner)?;21452146        // update balance2147        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2148            .checked_sub(rft_balance.fraction)2149            .ok_or(Error::<T>::NumOverflow)?;2150        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21512152        // Re-create owners list with sender removed2153        let index = token2154            .owner2155            .iter()2156            .position(|i| i.owner == *owner)2157            .expect("owned item is exists");2158        token.owner.remove(index);2159        let owner_count = token.owner.len();21602161        // Burn the token completely if this was the last (only) owner2162        if owner_count == 0 {2163            <ReFungibleItemList<T>>::remove(collection_id, item_id);2164            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2165        }2166        else {2167            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2168        }21692170        Ok(())2171    }21722173    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2174        let collection_id = collection.id;21752176        let item = <NftItemList<T>>::get(collection_id, item_id)2177            .ok_or(Error::<T>::TokenNotFound)?;2178        Self::remove_token_index(collection_id, item_id, &item.owner)?;21792180        // update balance2181        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2182            .checked_sub(1)2183            .ok_or(Error::<T>::NumOverflow)?;2184        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2185        <NftItemList<T>>::remove(collection_id, item_id);2186        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21872188        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2189        Ok(())2190    }21912192    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2193        let collection_id = collection.id;21942195        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2196        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21972198        // update balance2199        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2200            .checked_sub(value)2201            .ok_or(Error::<T>::NumOverflow)?;2202        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22032204        if balance.value - value > 0 {2205            balance.value -= value;2206            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2207        }2208        else {2209            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2210        }22112212        collection.log(2213            Vec::from([2214                eth::TRANSFER_FUNGIBLE_TOPIC,2215                eth::address_to_topic(owner.as_eth()),2216                eth::address_to_topic(&H160::default()),2217            ]),2218            abi_encode!(uint256(value.into())),2219        );2220        Ok(())2221    }22222223    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2224        Ok(<CollectionHandle<T>>::get(collection_id)2225            .ok_or(Error::<T>::CollectionNotFound)?)2226    }22272228    fn save_collection(collection: CollectionHandle<T>) {2229        <CollectionById<T>>::insert(collection.id, collection.into_inner());2230    }22312232    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2233        if collection.logs.is_empty() {2234            return Ok(())2235        }2236        T::EthereumTransactionSender::submit_logs_transaction(2237            eth::generate_transaction(collection.id, T::EthereumChainId::get()),2238            collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2239        )2240    }22412242    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2243        ensure!(2244            *subject == target_collection.owner,2245            Error::<T>::NoPermission2246        );22472248        Ok(())2249    }22502251    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2252        *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2253    }22542255    fn check_owner_or_admin_permissions(2256        collection: &CollectionHandle<T>,2257        subject: &T::CrossAccountId,2258    ) -> DispatchResult {2259        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22602261        Ok(())2262    }22632264    fn owned_amount(2265        subject: &T::CrossAccountId,2266        target_collection: &CollectionHandle<T>,2267        item_id: TokenId,2268    ) -> Option<u128> {2269        let collection_id = target_collection.id;22702271        match target_collection.mode {2272            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2273                .then(|| 1),2274            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2275                .value),2276            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2277                .owner2278                .iter()2279                .find(|i| i.owner == *subject)2280                .map(|i| i.fraction),2281            CollectionMode::Invalid => None,2282        }2283    }22842285    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2286        match target_collection.mode {2287            CollectionMode::Fungible(_) => true,2288            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2289        }2290    }22912292    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2293        let collection_id = collection.id;22942295        let mes = Error::<T>::AddresNotInWhiteList;2296        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22972298        Ok(())2299    }23002301    /// Check if token exists. In case of Fungible, check if there is an entry for 2302    /// the owner in fungible balances double map2303    fn token_exists(2304        target_collection: &CollectionHandle<T>,2305        item_id: TokenId,2306    ) -> DispatchResult {2307        let collection_id = target_collection.id;2308        let exists = match target_collection.mode2309        {2310            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2311            CollectionMode::Fungible(_)  => true,2312            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2313            _ => false2314        };23152316        ensure!(exists == true, Error::<T>::TokenNotFound);2317        Ok(())2318    }23192320    fn transfer_fungible(2321        collection: &CollectionHandle<T>,2322        value: u128,2323        owner: &T::CrossAccountId,2324        recipient: &T::CrossAccountId,2325    ) -> DispatchResult {2326        let collection_id = collection.id;23272328        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2329        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23302331        // Send balance to recipient (updates balanceOf of recipient)2332        Self::add_fungible_item(collection, recipient, value)?;23332334        // update balanceOf of sender2335        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23362337        // Reduce or remove sender2338        if balance.value == value {2339            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2340        }2341        else {2342            balance.value -= value;2343            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2344        }23452346        collection.log(2347            Vec::from([2348                eth::TRANSFER_FUNGIBLE_TOPIC,2349                eth::address_to_topic(owner.as_eth()),2350                eth::address_to_topic(recipient.as_eth()),2351            ]),2352            abi_encode!(uint256(value.into())),2353        );2354        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23552356        Ok(())2357    }23582359    fn transfer_refungible(2360        collection: &CollectionHandle<T>,2361        item_id: TokenId,2362        value: u128,2363        owner: T::CrossAccountId,2364        new_owner: T::CrossAccountId,2365    ) -> DispatchResult {2366        let collection_id = collection.id;2367        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2368            .ok_or(Error::<T>::TokenNotFound)?;23692370        let item = full_item2371            .owner2372            .iter()2373            .filter(|i| i.owner == owner)2374            .next()2375            .ok_or(Error::<T>::TokenNotFound)?;2376        let amount = item.fraction;23772378        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23792380        // update balance2381        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2382            .checked_sub(value)2383            .ok_or(Error::<T>::NumOverflow)?;2384        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23852386        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2387            .checked_add(value)2388            .ok_or(Error::<T>::NumOverflow)?;2389        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23902391        let old_owner = item.owner.clone();2392        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23932394        // transfer2395        if amount == value && !new_owner_has_account {2396            // change owner2397            // new owner do not have account2398            let mut new_full_item = full_item.clone();2399            new_full_item2400                .owner2401                .iter_mut()2402                .find(|i| i.owner == owner)2403                .expect("old owner does present in refungible")2404                .owner = new_owner.clone();2405            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24062407            // update index collection2408            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2409        } else {2410            let mut new_full_item = full_item.clone();2411            new_full_item2412                .owner2413                .iter_mut()2414                .find(|i| i.owner == owner)2415                .expect("old owner does present in refungible")2416                .fraction -= value;24172418            // separate amount2419            if new_owner_has_account {2420                // new owner has account2421                new_full_item2422                    .owner2423                    .iter_mut()2424                    .find(|i| i.owner == new_owner)2425                    .expect("new owner has account")2426                    .fraction += value;2427            } else {2428                // new owner do not have account2429                new_full_item.owner.push(Ownership {2430                    owner: new_owner.clone(),2431                    fraction: value,2432                });2433                Self::add_token_index(collection_id, item_id, &new_owner)?;2434            }24352436            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2437        }24382439        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24402441        Ok(())2442    }24432444    fn transfer_nft(2445        collection: &CollectionHandle<T>,2446        item_id: TokenId,2447        sender: T::CrossAccountId,2448        new_owner: T::CrossAccountId,2449    ) -> DispatchResult {2450        let collection_id = collection.id;2451        let mut item = <NftItemList<T>>::get(collection_id, item_id)2452            .ok_or(Error::<T>::TokenNotFound)?;24532454        ensure!(2455            sender == item.owner,2456            Error::<T>::MustBeTokenOwner2457        );24582459        // update balance2460        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2461            .checked_sub(1)2462            .ok_or(Error::<T>::NumOverflow)?;2463        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24642465        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2466            .checked_add(1)2467            .ok_or(Error::<T>::NumOverflow)?;2468        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24692470        // change owner2471        let old_owner = item.owner.clone();2472        item.owner = new_owner.clone();2473        <NftItemList<T>>::insert(collection_id, item_id, item);24742475        // update index collection2476        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24772478        collection.log(2479            Vec::from([2480                eth::TRANSFER_NFT_TOPIC,2481                eth::address_to_topic(sender.as_eth()),2482                eth::address_to_topic(new_owner.as_eth()),2483                eth::u32_to_topic(item_id),2484            ]),2485            abi_encode!(),2486        );2487        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24882489        Ok(())2490    }2491    2492    fn set_re_fungible_variable_data(2493        collection: &CollectionHandle<T>,2494        item_id: TokenId,2495        data: Vec<u8>2496    ) -> DispatchResult {2497        let collection_id = collection.id;2498        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2499            .ok_or(Error::<T>::TokenNotFound)?;25002501        item.variable_data = data;25022503        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25042505        Ok(())2506    }25072508    fn set_nft_variable_data(2509        collection: &CollectionHandle<T>,2510        item_id: TokenId,2511        data: Vec<u8>2512    ) -> DispatchResult {2513        let collection_id = collection.id;2514        let mut item = <NftItemList<T>>::get(collection_id, item_id)2515            .ok_or(Error::<T>::TokenNotFound)?;2516        2517        item.variable_data = data;25182519        <NftItemList<T>>::insert(collection_id, item_id, item);2520        2521        Ok(())2522    }25232524    fn init_collection(item: &Collection<T>) {2525        // check params2526        assert!(2527            item.decimal_points <= MAX_DECIMAL_POINTS,2528            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2529        );2530        assert!(2531            item.name.len() <= 64,2532            "Collection name can not be longer than 63 char"2533        );2534        assert!(2535            item.name.len() <= 256,2536            "Collection description can not be longer than 255 char"2537        );2538        assert!(2539            item.token_prefix.len() <= 16,2540            "Token prefix can not be longer than 15 char"2541        );25422543        // Generate next collection ID2544        let next_id = CreatedCollectionCount::get()2545            .checked_add(1)2546            .unwrap();25472548        CreatedCollectionCount::put(next_id);2549    }25502551    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2552        let current_index = <ItemListIndex>::get(collection_id)2553            .checked_add(1)2554            .unwrap();25552556        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25572558        <ItemListIndex>::insert(collection_id, current_index);25592560        // Update balance2561        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2562            .checked_add(1)2563            .unwrap();2564        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2565    }25662567    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2568        let current_index = <ItemListIndex>::get(collection_id)2569            .checked_add(1)2570            .unwrap();25712572        Self::add_token_index(collection_id, current_index, owner).unwrap();25732574        <ItemListIndex>::insert(collection_id, current_index);25752576        // Update balance2577        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2578            .checked_add(item.value)2579            .unwrap();2580        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2581    }25822583    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2584        let current_index = <ItemListIndex>::get(collection_id)2585            .checked_add(1)2586            .unwrap();25872588        let value = item.owner.first().unwrap().fraction;2589        let owner = item.owner.first().unwrap().owner.clone();25902591        Self::add_token_index(collection_id, current_index, &owner).unwrap();25922593        <ItemListIndex>::insert(collection_id, current_index);25942595        // Update balance2596        let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2597            .checked_add(value)2598            .unwrap();2599        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2600    }26012602    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2603        // add to account limit2604        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26052606            // bound Owned tokens by a single address2607            let count = <AccountItemCount<T>>::get(owner.as_sub());2608            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26092610            <AccountItemCount<T>>::insert(owner.as_sub(), count2611                .checked_add(1)2612                .ok_or(Error::<T>::NumOverflow)?);2613        }2614        else {2615            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2616        }26172618        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2619        if list_exists {2620            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2621            let item_contains = list.contains(&item_index.clone());26222623            if !item_contains {2624                list.push(item_index.clone());2625            }26262627            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2628        } else {2629            let mut itm = Vec::new();2630            itm.push(item_index.clone());2631            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2632        }26332634        Ok(())2635    }26362637    fn remove_token_index(2638        collection_id: CollectionId,2639        item_index: TokenId,2640        owner: &T::CrossAccountId,2641    ) -> DispatchResult {26422643        // update counter2644        <AccountItemCount<T>>::insert(owner.as_sub(), 2645            <AccountItemCount<T>>::get(owner.as_sub())2646            .checked_sub(1)2647            .ok_or(Error::<T>::NumOverflow)?);264826492650        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2651        if list_exists {2652            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2653            let item_contains = list.contains(&item_index.clone());26542655            if item_contains {2656                list.retain(|&item| item != item_index);2657                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2658            }2659        }26602661        Ok(())2662    }26632664    fn move_token_index(2665        collection_id: CollectionId,2666        item_index: TokenId,2667        old_owner: &T::CrossAccountId,2668        new_owner: &T::CrossAccountId,2669    ) -> DispatchResult {2670        Self::remove_token_index(collection_id, item_index, old_owner)?;2671        Self::add_token_index(collection_id, item_index, new_owner)?;26722673        Ok(())2674    }2675    2676    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2677        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26782679        Ok(())2680    }2681}26822683////////////////////////////////////////////////////////////////////////////////////////////////////2684// Economic models2685// #region26862687/// Fee multiplier.2688pub type Multiplier = FixedU128;26892690type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26912692/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2693/// in the queue.2694#[derive(Encode, Decode, Clone, Eq, PartialEq)]2695pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26962697impl<T: Config + Send + Sync> sp_std::fmt::Debug 2698    for ChargeTransactionPayment<T>2699{2700	#[cfg(feature = "std")]2701	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2702		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2703	}2704	#[cfg(not(feature = "std"))]2705	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2706		Ok(())2707	}2708}27092710impl<T: Config> ChargeTransactionPayment<T>2711where2712    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2713    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2714    T::AccountId: AsRef<[u8]>,2715    T::AccountId: UncheckedFrom<T::Hash>,2716{2717    fn traditional_fee(2718        len: usize,2719        info: &DispatchInfoOf<T::Call>,2720        tip: BalanceOf<T>,2721    ) -> BalanceOf<T>2722    where2723        T::Call: Dispatchable<Info = DispatchInfo>,2724    {2725        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2726    }27272728	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2729        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2730        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2731        let len_saturation = max_block_length as u64 / (len as u64).max(1);2732        let coefficient: BalanceOf<T> = weight_saturation2733            .min(len_saturation)2734            .saturated_into::<BalanceOf<T>>();2735        final_fee2736            .saturating_mul(coefficient)2737            .saturated_into::<TransactionPriority>()2738    }27392740    fn withdraw_fee(2741        &self,2742        who: &T::AccountId,2743        call: &T::Call,2744        info: &DispatchInfoOf<T::Call>,2745        len: usize,2746	) -> Result<2747		(2748			BalanceOf<T>,2749			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2750		),2751		TransactionValidityError,2752	> {2753        let tip = self.0;27542755        let fee = Self::traditional_fee(len, info, tip);27562757        // Only mess with balances if fee is not zero.2758        if fee.is_zero() {2759            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2760			.map(|i| (fee, i));2761        }27622763        // Determine who is paying transaction fee based on ecnomic model2764        // Parse call to extract collection ID and access collection sponsor2765        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2766            Some(Call::create_item(collection_id, _owner, _properties)) => {2767                let collection = <CollectionById<T>>::get(collection_id)?;27682769                // sponsor timeout2770                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27712772                let limit = collection.limits.sponsor_transfer_timeout;2773                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2774                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2775                    let limit_time = last_tx_block + limit.into();2776                    if block_number <= limit_time {2777                        return None;2778                    }2779                }2780                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27812782                // check free create limit2783                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2784                    collection.sponsorship.sponsor()2785                        .cloned()2786                } else {2787                    None2788                }2789            }2790            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2791                let collection = <CollectionById<T>>::get(collection_id)?;2792                2793                let mut sponsor_transfer = false;2794                if collection.sponsorship.confirmed() {27952796                    let collection_limits = collection.limits;2797                    let collection_mode = collection.mode;2798    2799                    // sponsor timeout2800                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2801                    sponsor_transfer = match collection_mode {2802                        CollectionMode::NFT => {2803    2804                            // get correct limit2805                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2806                                collection_limits.sponsor_transfer_timeout2807                            } else {2808                                ChainLimit::get().nft_sponsor_transfer_timeout2809                            };2810    2811                            let mut sponsored = true;2812                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2813                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2814                                let limit_time = last_tx_block + limit.into();2815                                if block_number <= limit_time {2816                                    sponsored = false;2817                                }2818                            }2819                            if sponsored {2820                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2821                            }28222823                            sponsored2824                        }2825                        CollectionMode::Fungible(_) => {2826    2827                            // get correct limit2828                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2829                                collection_limits.sponsor_transfer_timeout2830                            } else {2831                                ChainLimit::get().fungible_sponsor_transfer_timeout2832                            };2833    2834                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2835                            let mut sponsored = true;2836                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2837                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2838                                let limit_time = last_tx_block + limit.into();2839                                if block_number <= limit_time {2840                                    sponsored = false;2841                                }2842                            }2843                            if sponsored {2844                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2845                            }28462847                            sponsored2848                        }2849                        CollectionMode::ReFungible => {2850    2851                            // get correct limit2852                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2853                                collection_limits.sponsor_transfer_timeout2854                            } else {2855                                ChainLimit::get().refungible_sponsor_transfer_timeout2856                            };2857    2858                            let mut sponsored = true;2859                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2860                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2861                                let limit_time = last_tx_block + limit.into();2862                                if block_number <= limit_time {2863                                    sponsored = false;2864                                }2865                            }2866                            if sponsored {2867                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2868                            }28692870                            sponsored2871                        }2872                        _ => {2873                            false2874                        },2875                    };2876                }28772878                if !sponsor_transfer {2879                    None2880                } else {2881                    collection.sponsorship.sponsor()2882                        .cloned()2883                }2884            }28852886            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2887                let mut sponsor_metadata_changes = false;28882889                let collection = <CollectionById<T>>::get(collection_id)?;28902891                if2892                    collection.sponsorship.confirmed() &&2893                    // Can't sponsor fungible collection, this tx will be rejected2894                    // as invalid2895                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2896                    data.len() <= collection.limits.sponsored_data_size as usize2897                {2898                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2899                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29002901                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2902                            .map(|last_block| block_number - last_block > rate_limit)2903                            .unwrap_or(true) 2904                        {2905                            sponsor_metadata_changes = true;2906                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2907                        }2908                    }2909                }29102911                if !sponsor_metadata_changes {2912                    None2913                } else {2914                    collection.sponsorship.sponsor().cloned()2915                }2916            }29172918            _ => None,2919        })();29202921        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2922            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29232924                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29252926                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2927                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2928                  2929                if !owned_contract && white_list_enabled {2930                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2931                        return Err(InvalidTransaction::Call.into());2932                    }2933                }2934            },2935            _ => {},2936        }29372938        // Sponsor smart contracts2939        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29402941            // On instantiation: set the contract owner2942            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29432944                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2945                    &who,2946                    code_hash,2947                    salt,2948                );2949                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29502951                None2952            },29532954            // On instantiation with code: set the contract owner2955            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {29562957                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2958                    &who,2959                    &T::Hashing::hash(&_code),2960                    _salt,2961                );29622963                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29642965                None2966            }29672968            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2969            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29702971                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29722973                let mut sponsor_transfer = false;2974                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2975                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2976                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2977                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2978                    let limit_time = last_tx_block + rate_limit;29792980                    if block_number >= limit_time {2981                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2982                        sponsor_transfer = true;2983                    }2984                } else {2985                    sponsor_transfer = false;2986                }2987               2988                if sponsor_transfer {2989                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2990                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2991                            return Some(called_contract);2992                        }2993                    }2994                }29952996                None2997            },29982999            _ => None,3000        });30013002        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30033004		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3005			.map(|i| (fee, i))3006    }3007}300830093010impl<T: Config + Send + Sync> SignedExtension3011    for ChargeTransactionPayment<T>3012where3013    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3014    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3015    T::AccountId: AsRef<[u8]>,3016    T::AccountId: UncheckedFrom<T::Hash>,3017{3018    const IDENTIFIER: &'static str = "ChargeTransactionPayment";3019    type AccountId = T::AccountId;3020    type Call = T::Call;3021    type AdditionalSigned = ();3022    type Pre = (3023        // tip3024        BalanceOf<T>,3025        // who pays fee3026        Self::AccountId,3027		// imbalance resulting from withdrawing the fee3028		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3029    );3030    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3031        Ok(())3032    }30333034    fn validate(3035        &self,3036        who: &Self::AccountId,3037        call: &Self::Call,3038        info: &DispatchInfoOf<Self::Call>,3039        len: usize,3040    ) -> TransactionValidity {3041		let (fee, _) = self.withdraw_fee(who, call, info, len)?;3042		Ok(ValidTransaction {3043			priority: Self::get_priority(len, info, fee),3044			..Default::default()3045		})3046    }30473048    fn pre_dispatch(3049        self,3050        who: &Self::AccountId,3051        call: &Self::Call,3052        info: &DispatchInfoOf<Self::Call>,3053        len: usize,3054    ) -> Result<Self::Pre, TransactionValidityError> {3055        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3056        Ok((self.0, who.clone(), imbalance))3057    }30583059    fn post_dispatch(3060        pre: Self::Pre,3061        info: &DispatchInfoOf<Self::Call>,3062        post_info: &PostDispatchInfoOf<Self::Call>,3063        len: usize,3064        _result: &DispatchResult,3065    ) -> Result<(), TransactionValidityError> {3066		let (tip, who, imbalance) = pre;3067		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3068			len as u32,3069			info,3070			post_info,3071			tip,3072		);3073		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3074		Ok(())3075    }3076}30773078// #endregion30793080sp_api::decl_runtime_apis! {3081    pub trait NftApi {3082        /// Used for ethereum integration3083        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3084    }3085}
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16    construct_runtime, decl_event, decl_module, decl_storage, decl_error,17    dispatch::DispatchResult,18    ensure, fail, parameter_types,19    traits::{20        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21        Randomness, IsSubType, WithdrawReasons,22    },23    weights::{24        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26        WeightToFeePolynomial, DispatchClass,27    },28    StorageValue,29    transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_ethereum::EthereumTransactionSender;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::NftErcSupport;59pub use eth::account::*;6061pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;62pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;63pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;64pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6566// Structs67// #region6869pub type CollectionId = u32;70pub type TokenId = u32;71pub type DecimalPoints = u8;7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum CollectionMode {76    Invalid,77    NFT,78    // decimal points79    Fungible(DecimalPoints),80    ReFungible,81}8283impl Default for CollectionMode {84    fn default() -> Self {85        Self::Invalid86    }87}8889impl Into<u8> for CollectionMode {90    fn into(self) -> u8 {91        match self {92            CollectionMode::Invalid => 0,93            CollectionMode::NFT => 1,94            CollectionMode::Fungible(_) => 2,95            CollectionMode::ReFungible => 3,96        }97    }98}99100#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub enum AccessMode {103    Normal,104    WhiteList,105}106impl Default for AccessMode {107    fn default() -> Self {108        Self::Normal109    }110}111112#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]113#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]114pub enum SchemaVersion {115    ImageURL,116    Unique,117}118impl Default for SchemaVersion {119    fn default() -> Self {120        Self::ImageURL121    }122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct Ownership<AccountId> {127    pub owner: AccountId,128    pub fraction: u128,129}130131#[derive(Encode, Decode, Debug, Clone, PartialEq)]132#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]133pub enum SponsorshipState<AccountId> {134    /// The fees are applied to the transaction sender135    Disabled,136    Unconfirmed(AccountId),137    /// Transactions are sponsored by specified account138    Confirmed(AccountId),139}140141impl<AccountId> SponsorshipState<AccountId> {142    fn sponsor(&self) -> Option<&AccountId> {143        match self {144            Self::Confirmed(sponsor) => Some(sponsor),145            _ => None,146        }147    }148149    fn pending_sponsor(&self) -> Option<&AccountId> {150        match self {151            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),152            _ => None,153        }154    }155156    fn confirmed(&self) -> bool {157        matches!(self, Self::Confirmed(_))158    }159}160161impl<T> Default for SponsorshipState<T> {162    fn default() -> Self {163        Self::Disabled164    }165}166167#[derive(Encode, Decode, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct Collection<T: Config> {170    pub owner: T::AccountId,171    pub mode: CollectionMode,172    pub access: AccessMode,173    pub decimal_points: DecimalPoints,174    pub name: Vec<u16>,        // 64 include null escape char175    pub description: Vec<u16>, // 256 include null escape char176    pub token_prefix: Vec<u8>, // 16 include null escape char177    pub mint_mode: bool,178    pub offchain_schema: Vec<u8>,179    pub schema_version: SchemaVersion,180    pub sponsorship: SponsorshipState<T::AccountId>,181    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 182    pub variable_on_chain_schema: Vec<u8>, //183    pub const_on_chain_schema: Vec<u8>, //184}185186pub struct CollectionHandle<T: Config> {187    pub id: CollectionId,188    collection: Collection<T>,189    logs: eth::log::LogRecorder,190}191impl<T: Config> CollectionHandle<T> {192	pub fn get(id: CollectionId) -> Option<Self> {193		<CollectionById<T>>::get(id)194			.map(|collection| Self {195				id,196				collection,197                logs: eth::log::LogRecorder::default(),198			})199	}200    pub fn log(&self, topics: Vec<H256>, data: eth::abi::AbiWriter) {201        self.logs.log(topics, data)202    }203    pub fn into_inner(self) -> Collection<T> {204        self.collection.clone()205    }206}207208impl<T: Config> Deref for CollectionHandle<T> {209    type Target = Collection<T>;210211    fn deref(&self) -> &Self::Target {212        &self.collection213    }214}215216impl<T: Config> DerefMut for CollectionHandle<T> {217    fn deref_mut(&mut self) -> &mut Self::Target {218        &mut self.collection219    }220}221222#[derive(Encode, Decode, Debug, Clone, PartialEq)]223#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]224pub struct NftItemType<AccountId> {225    pub owner: AccountId,226    pub const_data: Vec<u8>,227    pub variable_data: Vec<u8>,228}229230#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]231#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]232pub struct FungibleItemType {233    pub value: u128,234}235236#[derive(Encode, Decode, Debug, Clone, PartialEq)]237#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]238pub struct ReFungibleItemType<AccountId> {239    pub owner: Vec<Ownership<AccountId>>,240    pub const_data: Vec<u8>,241    pub variable_data: Vec<u8>,242}243244// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246// pub struct VestingItem<AccountId, Moment> {247//     pub sender: AccountId,248//     pub recipient: AccountId,249//     pub collection_id: CollectionId,250//     pub item_id: TokenId,251//     pub amount: u64,252//     pub vesting_date: Moment,253// }254255#[derive(Encode, Decode, Debug, Clone, PartialEq)]256#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]257pub struct CollectionLimits<BlockNumber: Encode + Decode> {258    pub account_token_ownership_limit: u32,259    pub sponsored_data_size: u32,260    /// None - setVariableMetadata is not sponsored261    /// Some(v) - setVariableMetadata is sponsored 262    ///           if there is v block between txs263    pub sponsored_data_rate_limit: Option<BlockNumber>,264    pub token_limit: u32,265266    // Timeouts for item types in passed blocks267    pub sponsor_transfer_timeout: u32,268    pub owner_can_transfer: bool,269    pub owner_can_destroy: bool,270}271272impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {273    fn default() -> Self {274        Self { 275            account_token_ownership_limit: 10_000_000, 276            token_limit: u32::max_value(),277            sponsored_data_size: u32::MAX, 278            sponsored_data_rate_limit: None,279            sponsor_transfer_timeout: 14400,280            owner_can_transfer: true,281            owner_can_destroy: true282        }283    }284}285286#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]287#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]288pub struct ChainLimits {289    pub collection_numbers_limit: u32,290    pub account_token_ownership_limit: u32,291    pub collections_admins_limit: u64,292    pub custom_data_limit: u32,293294    // Timeouts for item types in passed blocks295    pub nft_sponsor_transfer_timeout: u32,296    pub fungible_sponsor_transfer_timeout: u32,297    pub refungible_sponsor_transfer_timeout: u32,298299    // Schema limits300    pub offchain_schema_limit: u32,301    pub variable_on_chain_schema_limit: u32,302    pub const_on_chain_schema_limit: u32,303}304305pub trait WeightInfo {306	fn create_collection() -> Weight;307	fn destroy_collection() -> Weight;308	fn add_to_white_list() -> Weight;309	fn remove_from_white_list() -> Weight;310    fn set_public_access_mode() -> Weight;311    fn set_mint_permission() -> Weight;312    fn change_collection_owner() -> Weight;313    fn add_collection_admin() -> Weight;314    fn remove_collection_admin() -> Weight;315    fn set_collection_sponsor() -> Weight;316    fn confirm_sponsorship() -> Weight;317    fn remove_collection_sponsor() -> Weight;318    fn create_item(s: usize) -> Weight;319    fn burn_item() -> Weight;320    fn transfer() -> Weight;321    fn approve() -> Weight;322    fn transfer_from() -> Weight;323    fn set_offchain_schema() -> Weight;324    fn set_const_on_chain_schema() -> Weight;325    fn set_variable_on_chain_schema() -> Weight;326    fn set_variable_meta_data() -> Weight;327    fn enable_contract_sponsoring() -> Weight;328    fn set_schema_version() -> Weight;329    fn set_chain_limits() -> Weight;330    fn set_contract_sponsoring_rate_limit() -> Weight;331    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;332    fn toggle_contract_white_list() -> Weight;333    fn add_to_contract_white_list() -> Weight;334    fn remove_from_contract_white_list() -> Weight;335    fn set_collection_limits() -> Weight;336}337338#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]339#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]340pub struct CreateNftData {341    pub const_data: Vec<u8>,342    pub variable_data: Vec<u8>,343}344345#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]346#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]347pub struct CreateFungibleData {348    pub value: u128,349}350351#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]352#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]353pub struct CreateReFungibleData {354    pub const_data: Vec<u8>,355    pub variable_data: Vec<u8>,356    pub pieces: u128,357}358359#[derive(Encode, Decode, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub enum CreateItemData {362    NFT(CreateNftData),363    Fungible(CreateFungibleData),364    ReFungible(CreateReFungibleData),365}366367impl CreateItemData {368    pub fn len(&self) -> usize {369        let len = match self {370            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),371            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),372            _ => 0373        };374        375        return len;376    }377}378379impl From<CreateNftData> for CreateItemData {380    fn from(item: CreateNftData) -> Self {381        CreateItemData::NFT(item)382    }383}384385impl From<CreateReFungibleData> for CreateItemData {386    fn from(item: CreateReFungibleData) -> Self {387        CreateItemData::ReFungible(item)388    }389}390391impl From<CreateFungibleData> for CreateItemData {392    fn from(item: CreateFungibleData) -> Self {393        CreateItemData::Fungible(item)394    }395}396397398decl_error! {399	/// Error for non-fungible-token module.400	pub enum Error for Module<T: Config> {401        /// Total collections bound exceeded.402        TotalCollectionsLimitExceeded,403		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.404        CollectionDecimalPointLimitExceeded, 405        /// Collection name can not be longer than 63 char.406        CollectionNameLimitExceeded, 407        /// Collection description can not be longer than 255 char.408        CollectionDescriptionLimitExceeded, 409        /// Token prefix can not be longer than 15 char.410        CollectionTokenPrefixLimitExceeded,411        /// This collection does not exist.412        CollectionNotFound,413        /// Item not exists.414        TokenNotFound,415        /// Admin not found416        AdminNotFound,417        /// Arithmetic calculation overflow.418        NumOverflow,       419        /// Account already has admin role.420        AlreadyAdmin,  421        /// You do not own this collection.422        NoPermission,423        /// This address is not set as sponsor, use setCollectionSponsor first.424        ConfirmUnsetSponsorFail,425        /// Collection is not in mint mode.426        PublicMintingNotAllowed,427        /// Sender parameter and item owner must be equal.428        MustBeTokenOwner,429        /// Item balance not enough.430        TokenValueTooLow,431        /// Size of item is too large.432        NftSizeLimitExceeded,433        /// No approve found434        ApproveNotFound,435        /// Requested value more than approved.436        TokenValueNotEnough,437        /// Only approved addresses can call this method.438        ApproveRequired,439        /// Address is not in white list.440        AddresNotInWhiteList,441        /// Number of collection admins bound exceeded.442        CollectionAdminsLimitExceeded,443        /// Owned tokens by a single address bound exceeded.444        AddressOwnershipLimitExceeded,445        /// Length of items properties must be greater than 0.446        EmptyArgument,447        /// const_data exceeded data limit.448        TokenConstDataLimitExceeded,449        /// variable_data exceeded data limit.450        TokenVariableDataLimitExceeded,451        /// Not NFT item data used to mint in NFT collection.452        NotNftDataUsedToMintNftCollectionToken,453        /// Not Fungible item data used to mint in Fungible collection.454        NotFungibleDataUsedToMintFungibleCollectionToken,455        /// Not Re Fungible item data used to mint in Re Fungible collection.456        NotReFungibleDataUsedToMintReFungibleCollectionToken,457        /// Unexpected collection type.458        UnexpectedCollectionType,459        /// Can't store metadata in fungible tokens.460        CantStoreMetadataInFungibleTokens,461        /// Collection token limit exceeded462        CollectionTokenLimitExceeded,463        /// Account token limit exceeded per collection464        AccountTokenLimitExceeded,465        /// Collection limit bounds per collection exceeded466        CollectionLimitBoundsExceeded,467        /// Tried to enable permissions which are only permitted to be disabled468        OwnerPermissionsCantBeReverted,469        /// Schema data size limit bound exceeded470        SchemaDataLimitExceeded,471        /// Maximum refungibility exceeded472        WrongRefungiblePieces,473        /// createRefungible should be called with one owner474        BadCreateRefungibleCall,475	}476}477478pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {479    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;480481    /// Weight information for extrinsics in this pallet.482	type WeightInfo: WeightInfo;483484    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;485    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;486    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;487488	type CrossAccountId: CrossAccountId<Self::AccountId>;489    type Currency: Currency<Self::AccountId>;490    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;491    type TreasuryAccountId: Get<Self::AccountId>;492493    type EthereumChainId: Get<u64>;494    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;495}496497#[cfg(feature = "runtime-benchmarks")]498mod benchmarking;499500// #endregion501502// # Used definitions503//504// ## User control levels505//506// chain-controlled - key is uncontrolled by user507//                    i.e autoincrementing index508//                    can use non-cryptographic hash509// real - key is controlled by user510//        but it is hard to generate enough colliding values, i.e owner of signed txs511//        can use non-cryptographic hash512// controlled - key is completly controlled by users513//              i.e maps with mutable keys514//              should use cryptographic hash515//516// ## User control level downgrade reasons517//518// ?1 - chain-controlled -> controlled519//      collections/tokens can be destroyed, resulting in massive holes520// ?2 - chain-controlled -> controlled521//      same as ?1, but can be only added, resulting in easier exploitation522// ?3 - real -> controlled523//      no confirmation required, so addresses can be easily generated524decl_storage! {525    trait Store for Module<T: Config> as Nft {526527        //#region Private members528        /// Id of next collection529        CreatedCollectionCount: u32;530        /// Used for migrations531        ChainVersion: u64;532        /// Id of last collection token533        /// Collection id (controlled?1)534        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;535        //#endregion536537        //#region Chain limits struct538        pub ChainLimit get(fn chain_limit) config(): ChainLimits;539        //#endregion540541        //#region Bound counters542        /// Amount of collections destroyed, used for total amount tracking with543        /// CreatedCollectionCount544        DestroyedCollectionCount: u32;545        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)546        /// Account id (real)547        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;548        //#endregion549550        //#region Basic collections551        /// Collection info552        /// Collection id (controlled?1)553        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;554        /// List of collection admins555        /// Collection id (controlled?2)556        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;557        /// Whitelisted collection users558        /// Collection id (controlled?2), user id (controlled?3)559        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;560        //#endregion561562        /// How many of collection items user have563        /// Collection id (controlled?2), account id (real)564        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;565566        /// Amount of items which spender can transfer out of owners account (via transferFrom)567        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))568        /// TODO: Off chain worker should remove from this map when token gets removed569        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;570571        //#region Item collections572        /// Collection id (controlled?2), token id (controlled?1)573        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;574        /// Collection id (controlled?2), owner (controlled?2)575        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;576        /// Collection id (controlled?2), token id (controlled?1)577        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;578        //#endregion579580        //#region Index list581        /// Collection id (controlled?2), tokens owner (controlled?2)582        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;583        //#endregion584585        //#region Tokens transfer rate limit baskets586        /// (Collection id (controlled?2), who created (real))587        /// TODO: Off chain worker should remove from this map when collection gets removed588        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;589        /// Collection id (controlled?2), token id (controlled?2)590        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;591        /// Collection id (controlled?2), owning user (real)592        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;593        /// Collection id (controlled?2), token id (controlled?2)594        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;595        //#endregion596597        /// Variable metadata sponsoring598        /// Collection id (controlled?2), token id (controlled?2)599        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;600      601        //#region Contract Sponsorship and Ownership602        /// Contract address (real)603        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;604        /// Contract address (real)605        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;606        /// (Contract address(real), caller (real))607        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;608        /// Contract address (real)609        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;610        /// Contract address (real)611        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 612        /// Contract address (real) => Whitelisted user (controlled?3)613        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 614        //#endregion615    }616    add_extra_genesis {617        build(|config: &GenesisConfig<T>| {618            // Modification of storage619            for (_num, _c) in &config.collection_id {620                <Module<T>>::init_collection(_c);621            }622623            for (_num, _c, _i) in &config.nft_item_id {624                <Module<T>>::init_nft_token(*_c, _i);625            }626627            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {628                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);629            }630631            for (_num, _c, _i) in &config.refungible_item_id {632                <Module<T>>::init_refungible_token(*_c, _i);633            }634        })635    }636}637638decl_event!(639    pub enum Event<T>640    where641        AccountId = <T as frame_system::Config>::AccountId,642        CrossAccountId = <T as Config>::CrossAccountId,643    {644        /// New collection was created645        /// 646        /// # Arguments647        /// 648        /// * collection_id: Globally unique identifier of newly created collection.649        /// 650        /// * mode: [CollectionMode] converted into u8.651        /// 652        /// * account_id: Collection owner.653        CollectionCreated(CollectionId, u8, AccountId),654655        /// New item was created.656        /// 657        /// # Arguments658        /// 659        /// * collection_id: Id of the collection where item was created.660        /// 661        /// * item_id: Id of an item. Unique within the collection.662        ///663        /// * recipient: Owner of newly created item 664        ItemCreated(CollectionId, TokenId, CrossAccountId),665666        /// Collection item was burned.667        /// 668        /// # Arguments669        /// 670        /// collection_id.671        /// 672        /// item_id: Identifier of burned NFT.673        ItemDestroyed(CollectionId, TokenId),674675        /// Item was transferred676        ///677        /// * collection_id: Id of collection to which item is belong678        ///679        /// * item_id: Id of an item680        ///681        /// * sender: Original owner of item682        ///683        /// * recipient: New owner of item684        ///685        /// * amount: Always 1 for NFT686        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),687688        /// * collection_id689        ///690        /// * item_id691        ///692        /// * sender693        ///694        /// * spender695        ///696        /// * amount697        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),698    }699);700701decl_module! {702    pub struct Module<T: Config> for enum Call 703    where 704        origin: T::Origin705    {706        fn deposit_event() = default;707        type Error = Error<T>;708709        fn on_initialize(now: T::BlockNumber) -> Weight {710            0711        }712713        /// 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.714        /// 715        /// # Permissions716        /// 717        /// * Anyone.718        /// 719        /// # Arguments720        /// 721        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.722        /// 723        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.724        /// 725        /// * token_prefix: UTF-8 string with token prefix.726        /// 727        /// * mode: [CollectionMode] collection type and type dependent data.728        // returns collection ID729        #[weight = <T as Config>::WeightInfo::create_collection()]730        #[transactional]731        pub fn create_collection(origin,732                                 collection_name: Vec<u16>,733                                 collection_description: Vec<u16>,734                                 token_prefix: Vec<u8>,735                                 mode: CollectionMode) -> DispatchResult {736737            // Anyone can create a collection738            let who = ensure_signed(origin)?;739740            // Take a (non-refundable) deposit of collection creation741            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();742            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(743                &T::TreasuryAccountId::get(),744                T::CollectionCreationPrice::get(),745            ));746            <T as Config>::Currency::settle(747                &who,748                imbalance,749                WithdrawReasons::TRANSFER,750                ExistenceRequirement::KeepAlive,751            ).map_err(|_| Error::<T>::NoPermission)?;752753            let decimal_points = match mode {754                CollectionMode::Fungible(points) => points,755                _ => 0756            };757758            let chain_limit = ChainLimit::get();759760            let created_count = CreatedCollectionCount::get();761            let destroyed_count = DestroyedCollectionCount::get();762763            // bound Total number of collections764            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);765766            // check params767            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);768            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);769            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);770            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);771772            // Generate next collection ID773            let next_id = created_count774                .checked_add(1)775                .ok_or(Error::<T>::NumOverflow)?;776777            CreatedCollectionCount::put(next_id);778779            let limits = CollectionLimits {780                sponsored_data_size: chain_limit.custom_data_limit,781                ..Default::default()782            };783784            // Create new collection785            let new_collection = Collection {786                owner: who.clone(),787                name: collection_name,788                mode: mode.clone(),789                mint_mode: false,790                access: AccessMode::Normal,791                description: collection_description,792                decimal_points: decimal_points,793                token_prefix: token_prefix,794                offchain_schema: Vec::new(),795                schema_version: SchemaVersion::ImageURL,796                sponsorship: SponsorshipState::Disabled,797                variable_on_chain_schema: Vec::new(),798                const_on_chain_schema: Vec::new(),799                limits,800            };801802            // Add new collection to map803            <CollectionById<T>>::insert(next_id, new_collection);804805            // call event806            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));807808            Ok(())809        }810811        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.812        /// 813        /// # Permissions814        /// 815        /// * Collection Owner.816        /// 817        /// # Arguments818        /// 819        /// * collection_id: collection to destroy.820        #[weight = <T as Config>::WeightInfo::destroy_collection()]821        #[transactional]822        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {823824            let sender = ensure_signed(origin)?;825            let collection = Self::get_collection(collection_id)?;826            Self::check_owner_permissions(&collection, &sender)?;827            if !collection.limits.owner_can_destroy {828                fail!(Error::<T>::NoPermission);829            }830831            <AddressTokens<T>>::remove_prefix(collection_id);832            <Allowances<T>>::remove_prefix(collection_id);833            <Balance<T>>::remove_prefix(collection_id);834            <ItemListIndex>::remove(collection_id);835            <AdminList<T>>::remove(collection_id);836            <CollectionById<T>>::remove(collection_id);837            <WhiteList<T>>::remove_prefix(collection_id);838839            <NftItemList<T>>::remove_prefix(collection_id);840            <FungibleItemList<T>>::remove_prefix(collection_id);841            <ReFungibleItemList<T>>::remove_prefix(collection_id);842843            <NftTransferBasket<T>>::remove_prefix(collection_id);844            <FungibleTransferBasket<T>>::remove_prefix(collection_id);845            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);846847            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);848849            DestroyedCollectionCount::put(DestroyedCollectionCount::get()850                .checked_add(1)851                .ok_or(Error::<T>::NumOverflow)?);852853            Ok(())854        }855856        /// Add an address to white list.857        /// 858        /// # Permissions859        /// 860        /// * Collection Owner861        /// * Collection Admin862        /// 863        /// # Arguments864        /// 865        /// * collection_id.866        /// 867        /// * address.868        #[weight = <T as Config>::WeightInfo::add_to_white_list()]869        #[transactional]870        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{871872            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);873            let collection = Self::get_collection(collection_id)?;874875            Self::toggle_white_list_internal(876                &sender,877                &collection,878                &address,879                true,880            )?;881882            Ok(())883        }884885        /// Remove an address from white list.886        /// 887        /// # Permissions888        /// 889        /// * Collection Owner890        /// * Collection Admin891        /// 892        /// # Arguments893        /// 894        /// * collection_id.895        /// 896        /// * address.897        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]898        #[transactional]899        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{900901            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902            let collection = Self::get_collection(collection_id)?;903904            Self::toggle_white_list_internal(905                &sender,906                &collection,907                &address,908                false,909            )?;910911            Ok(())912        }913914        /// Toggle between normal and white list access for the methods with access for `Anyone`.915        /// 916        /// # Permissions917        /// 918        /// * Collection Owner.919        /// 920        /// # Arguments921        /// 922        /// * collection_id.923        /// 924        /// * mode: [AccessMode]925        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]926        #[transactional]927        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult928        {929            let sender = ensure_signed(origin)?;930931            let mut target_collection = Self::get_collection(collection_id)?;932            Self::check_owner_permissions(&target_collection, &sender)?;933            target_collection.access = mode;934            Self::save_collection(target_collection);935936            Ok(())937        }938939        /// Allows Anyone to create tokens if:940        /// * White List is enabled, and941        /// * Address is added to white list, and942        /// * This method was called with True parameter943        /// 944        /// # Permissions945        /// * Collection Owner946        ///947        /// # Arguments948        /// 949        /// * collection_id.950        /// 951        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.952        #[weight = <T as Config>::WeightInfo::set_mint_permission()]953        #[transactional]954        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult955        {956            let sender = ensure_signed(origin)?;957958            let mut target_collection = Self::get_collection(collection_id)?;959            Self::check_owner_permissions(&target_collection, &sender)?;960            target_collection.mint_mode = mint_permission;961            Self::save_collection(target_collection);962963            Ok(())964        }965966        /// Change the owner of the collection.967        /// 968        /// # Permissions969        /// 970        /// * Collection Owner.971        /// 972        /// # Arguments973        /// 974        /// * collection_id.975        /// 976        /// * new_owner.977        #[weight = <T as Config>::WeightInfo::change_collection_owner()]978        #[transactional]979        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {980981            let sender = ensure_signed(origin)?;982            let mut target_collection = Self::get_collection(collection_id)?;983            Self::check_owner_permissions(&target_collection, &sender)?;984            target_collection.owner = new_owner;985            Self::save_collection(target_collection);986987            Ok(())988        }989990        /// Adds an admin of the Collection.991        /// 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. 992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner.996        /// * Collection Admin.997        /// 998        /// # Arguments999        /// 1000        /// * collection_id: ID of the Collection to add admin for.1001        /// 1002        /// * new_admin_id: Address of new admin to add.1003        #[weight = <T as Config>::WeightInfo::add_collection_admin()]1004        #[transactional]1005        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1006            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1007            let collection = Self::get_collection(collection_id)?;1008            Self::check_owner_or_admin_permissions(&collection, &sender)?;1009            let mut admin_arr = <AdminList<T>>::get(collection_id);10101011            match admin_arr.binary_search(&new_admin_id) {1012                Ok(_) => {},1013                Err(idx) => {1014                    let limits = ChainLimit::get();1015                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1016                    admin_arr.insert(idx, new_admin_id);1017                    <AdminList<T>>::insert(collection_id, admin_arr);1018                }1019            }1020            Ok(())1021        }10221023        /// 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.1024        ///1025        /// # Permissions1026        /// 1027        /// * Collection Owner.1028        /// * Collection Admin.1029        /// 1030        /// # Arguments1031        /// 1032        /// * collection_id: ID of the Collection to remove admin for.1033        /// 1034        /// * account_id: Address of admin to remove.1035        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1036        #[transactional]1037        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1038            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039            let collection = Self::get_collection(collection_id)?;1040            Self::check_owner_or_admin_permissions(&collection, &sender)?;1041            let mut admin_arr = <AdminList<T>>::get(collection_id);10421043            match admin_arr.binary_search(&account_id) {1044                Ok(idx) => {1045                    admin_arr.remove(idx);1046                    <AdminList<T>>::insert(collection_id, admin_arr);1047                },1048                Err(_) => {}1049            }1050            Ok(())1051        }10521053        /// # Permissions1054        /// 1055        /// * Collection Owner1056        /// 1057        /// # Arguments1058        /// 1059        /// * collection_id.1060        /// 1061        /// * new_sponsor.1062        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1063        #[transactional]1064        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1065            let sender = ensure_signed(origin)?;1066            let mut target_collection = Self::get_collection(collection_id)?;1067            Self::check_owner_permissions(&target_collection, &sender)?;10681069            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1070            Self::save_collection(target_collection);10711072            Ok(())1073        }10741075        /// # Permissions1076        /// 1077        /// * Sponsor.1078        /// 1079        /// # Arguments1080        /// 1081        /// * collection_id.1082        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1083        #[transactional]1084        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1085            let sender = ensure_signed(origin)?;10861087            let mut target_collection = Self::get_collection(collection_id)?;1088            ensure!(1089                target_collection.sponsorship.pending_sponsor() == Some(&sender),1090                Error::<T>::ConfirmUnsetSponsorFail1091            );10921093            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1094            Self::save_collection(target_collection);10951096            Ok(())1097        }10981099        /// Switch back to pay-per-own-transaction model.1100        ///1101        /// # Permissions1102        ///1103        /// * Collection owner.1104        /// 1105        /// # Arguments1106        /// 1107        /// * collection_id.1108        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1109        #[transactional]1110        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1111            let sender = ensure_signed(origin)?;11121113            let mut target_collection = Self::get_collection(collection_id)?;1114            Self::check_owner_permissions(&target_collection, &sender)?;11151116            target_collection.sponsorship = SponsorshipState::Disabled;1117            Self::save_collection(target_collection);11181119            Ok(())1120        }11211122        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1123        /// 1124        /// # Permissions1125        /// 1126        /// * Collection Owner.1127        /// * Collection Admin.1128        /// * Anyone if1129        ///     * White List is enabled, and1130        ///     * Address is added to white list, and1131        ///     * MintPermission is enabled (see SetMintPermission method)1132        /// 1133        /// # Arguments1134        /// 1135        /// * collection_id: ID of the collection.1136        /// 1137        /// * owner: Address, initial owner of the NFT.1138        ///1139        /// * data: Token data to store on chain.1140        // #[weight =1141        // (130_000_000 as Weight)1142        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1143        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1144        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11451146        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1147        #[transactional]1148        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1149            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1150            let collection = Self::get_collection(collection_id)?;11511152            Self::create_item_internal(&sender, &collection, &owner, data)?;11531154            Self::submit_logs(collection)?;1155            Ok(())1156        }11571158        /// This method creates multiple items in a collection created with CreateCollection method.1159        /// 1160        /// # Permissions1161        /// 1162        /// * Collection Owner.1163        /// * Collection Admin.1164        /// * Anyone if1165        ///     * White List is enabled, and1166        ///     * Address is added to white list, and1167        ///     * MintPermission is enabled (see SetMintPermission method)1168        /// 1169        /// # Arguments1170        /// 1171        /// * collection_id: ID of the collection.1172        /// 1173        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1174        /// 1175        /// * owner: Address, initial owner of the NFT.1176        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1177                               .map(|data| { data.len() })1178                               .sum())]1179        #[transactional]1180        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11811182            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1183            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1184            let collection = Self::get_collection(collection_id)?;11851186            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11871188            Self::submit_logs(collection)?;1189            Ok(())1190        }11911192        /// Destroys a concrete instance of NFT.1193        /// 1194        /// # Permissions1195        /// 1196        /// * Collection Owner.1197        /// * Collection Admin.1198        /// * Current NFT Owner.1199        /// 1200        /// # Arguments1201        /// 1202        /// * collection_id: ID of the collection.1203        /// 1204        /// * item_id: ID of NFT to burn.1205        #[weight = <T as Config>::WeightInfo::burn_item()]1206        #[transactional]1207        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12081209            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210            let target_collection = Self::get_collection(collection_id)?;12111212            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12131214            Self::submit_logs(target_collection)?;1215            Ok(())1216        }12171218        /// Change ownership of the token.1219        /// 1220        /// # Permissions1221        /// 1222        /// * Collection Owner1223        /// * Collection Admin1224        /// * Current NFT owner1225        ///1226        /// # Arguments1227        /// 1228        /// * recipient: Address of token recipient.1229        /// 1230        /// * collection_id.1231        /// 1232        /// * item_id: ID of the item1233        ///     * Non-Fungible Mode: Required.1234        ///     * Fungible Mode: Ignored.1235        ///     * Re-Fungible Mode: Required.1236        /// 1237        /// * value: Amount to transfer.1238        ///     * Non-Fungible Mode: Ignored1239        ///     * Fungible Mode: Must specify transferred amount1240        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1241        #[weight = <T as Config>::WeightInfo::transfer()]1242        #[transactional]1243        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1244            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1245            let collection = Self::get_collection(collection_id)?;12461247            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12481249            Self::submit_logs(collection)?;1250            Ok(())1251        }12521253        /// Set, change, or remove approved address to transfer the ownership of the NFT.1254        /// 1255        /// # Permissions1256        /// 1257        /// * Collection Owner1258        /// * Collection Admin1259        /// * Current NFT owner1260        /// 1261        /// # Arguments1262        /// 1263        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1264        /// 1265        /// * collection_id.1266        /// 1267        /// * item_id: ID of the item.1268        #[weight = <T as Config>::WeightInfo::approve()]1269        #[transactional]1270        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1271            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1272            let collection = Self::get_collection(collection_id)?;12731274            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12751276            Self::submit_logs(collection)?;1277            Ok(())1278        }1279        1280        /// 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.1281        /// 1282        /// # Permissions1283        /// * Collection Owner1284        /// * Collection Admin1285        /// * Current NFT owner1286        /// * Address approved by current NFT owner1287        /// 1288        /// # Arguments1289        /// 1290        /// * from: Address that owns token.1291        /// 1292        /// * recipient: Address of token recipient.1293        /// 1294        /// * collection_id.1295        /// 1296        /// * item_id: ID of the item.1297        /// 1298        /// * value: Amount to transfer.1299        #[weight = <T as Config>::WeightInfo::transfer_from()]1300        #[transactional]1301        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1302            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1303            let collection = Self::get_collection(collection_id)?;13041305            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13061307            Self::submit_logs(collection)?;1308            Ok(())1309        }1310        // #[weight = 0]1311        //     // let no_perm_mes = "You do not have permissions to modify this collection";1312        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1313        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1314        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13151316        //     // // on_nft_received  call13171318        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13191320        //     Ok(())1321        // }13221323        /// Set off-chain data schema.1324        /// 1325        /// # Permissions1326        /// 1327        /// * Collection Owner1328        /// * Collection Admin1329        /// 1330        /// # Arguments1331        /// 1332        /// * collection_id.1333        /// 1334        /// * schema: String representing the offchain data schema.1335        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1336        #[transactional]1337        pub fn set_variable_meta_data (1338            origin,1339            collection_id: CollectionId,1340            item_id: TokenId,1341            data: Vec<u8>1342        ) -> DispatchResult {1343            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1344            1345            let collection = Self::get_collection(collection_id)?;13461347            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13481349            Ok(())1350        }1351 1352        /// Set schema standard1353        /// ImageURL1354        /// Unique1355        /// 1356        /// # Permissions1357        /// 1358        /// * Collection Owner1359        /// * Collection Admin1360        /// 1361        /// # Arguments1362        /// 1363        /// * collection_id.1364        /// 1365        /// * schema: SchemaVersion: enum1366        #[weight = <T as Config>::WeightInfo::set_schema_version()]1367        #[transactional]1368        pub fn set_schema_version(1369            origin,1370            collection_id: CollectionId,1371            version: SchemaVersion1372        ) -> DispatchResult {1373            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1374            let mut target_collection = Self::get_collection(collection_id)?;1375            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1376            target_collection.schema_version = version;1377            Self::save_collection(target_collection);13781379            Ok(())1380        }13811382        /// Set off-chain data schema.1383        /// 1384        /// # Permissions1385        /// 1386        /// * Collection Owner1387        /// * Collection Admin1388        /// 1389        /// # Arguments1390        /// 1391        /// * collection_id.1392        /// 1393        /// * schema: String representing the offchain data schema.1394        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1395        #[transactional]1396        pub fn set_offchain_schema(1397            origin,1398            collection_id: CollectionId,1399            schema: Vec<u8>1400        ) -> DispatchResult {1401            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1402            let mut target_collection = Self::get_collection(collection_id)?;1403            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14041405            // check schema limit1406            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14071408            target_collection.offchain_schema = schema;1409            Self::save_collection(target_collection);14101411            Ok(())1412        }14131414        /// Set const on-chain data schema.1415        /// 1416        /// # Permissions1417        /// 1418        /// * Collection Owner1419        /// * Collection Admin1420        /// 1421        /// # Arguments1422        /// 1423        /// * collection_id.1424        /// 1425        /// * schema: String representing the const on-chain data schema.1426        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1427        #[transactional]1428        pub fn set_const_on_chain_schema (1429            origin,1430            collection_id: CollectionId,1431            schema: Vec<u8>1432        ) -> DispatchResult {1433            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1434            let mut target_collection = Self::get_collection(collection_id)?;1435            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14361437            // check schema limit1438            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14391440            target_collection.const_on_chain_schema = schema;1441            Self::save_collection(target_collection);14421443            Ok(())1444        }14451446        /// Set variable on-chain data schema.1447        /// 1448        /// # Permissions1449        /// 1450        /// * Collection Owner1451        /// * Collection Admin1452        /// 1453        /// # Arguments1454        /// 1455        /// * collection_id.1456        /// 1457        /// * schema: String representing the variable on-chain data schema.1458        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1459        #[transactional]1460        pub fn set_variable_on_chain_schema (1461            origin,1462            collection_id: CollectionId,1463            schema: Vec<u8>1464        ) -> DispatchResult {1465            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1466            let mut target_collection = Self::get_collection(collection_id)?;1467            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14681469            // check schema limit1470            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14711472            target_collection.variable_on_chain_schema = schema;1473            Self::save_collection(target_collection);14741475            Ok(())1476        }14771478        // Sudo permissions function1479        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1480        #[transactional]1481        pub fn set_chain_limits(1482            origin,1483            limits: ChainLimits1484        ) -> DispatchResult {14851486            #[cfg(not(feature = "runtime-benchmarks"))]1487            ensure_root(origin)?;14881489            <ChainLimit>::put(limits);1490            Ok(())1491        }14921493        /// Enable smart contract self-sponsoring.1494        /// 1495        /// # Permissions1496        /// 1497        /// * Contract Owner1498        /// 1499        /// # Arguments1500        /// 1501        /// * contract address1502        /// * enable flag1503        /// 1504        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1505        #[transactional]1506        pub fn enable_contract_sponsoring(1507            origin,1508            contract_address: T::AccountId,1509            enable: bool1510        ) -> DispatchResult {15111512            let sender = ensure_signed(origin)?;15131514            #[cfg(feature = "runtime-benchmarks")]1515            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15161517            Self::ensure_contract_owned(sender, &contract_address)?;15181519            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1520            Ok(())1521        }15221523        /// Set the rate limit for contract sponsoring to specified number of blocks.1524        /// 1525        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1526        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1527        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1528        /// from contract endowment if there are at least B blocks between such transactions. 1529        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1530        /// 1531        /// # Permissions1532        /// 1533        /// * Contract Owner1534        /// 1535        /// # Arguments1536        /// 1537        /// -`contract_address`: Address of the contract to sponsor1538        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1539        /// 1540        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1541        #[transactional]1542        pub fn set_contract_sponsoring_rate_limit(1543            origin,1544            contract_address: T::AccountId,1545            rate_limit: T::BlockNumber1546        ) -> DispatchResult {1547            let sender = ensure_signed(origin)?;15481549            #[cfg(feature = "runtime-benchmarks")]1550            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15511552            Self::ensure_contract_owned(sender, &contract_address)?;1553            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1554            Ok(())1555        }15561557        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1558        /// 1559        /// # Permissions1560        /// 1561        /// * Address that deployed smart contract.1562        /// 1563        /// # Arguments1564        /// 1565        /// -`contract_address`: Address of the contract.1566        /// 1567        /// - `enable`: .  1568        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1569        #[transactional]1570        pub fn toggle_contract_white_list(1571            origin,1572            contract_address: T::AccountId,1573            enable: bool1574        ) -> DispatchResult {1575            let sender = ensure_signed(origin)?;15761577            #[cfg(feature = "runtime-benchmarks")]1578            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15791580            Self::ensure_contract_owned(sender, &contract_address)?;1581            if enable {1582                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1583            } else {1584                <ContractWhiteListEnabled<T>>::remove(contract_address);1585            }1586            Ok(())1587        }1588        1589        /// Add an address to smart contract white list.1590        /// 1591        /// # Permissions1592        /// 1593        /// * Address that deployed smart contract.1594        /// 1595        /// # Arguments1596        /// 1597        /// -`contract_address`: Address of the contract.1598        ///1599        /// -`account_address`: Address to add.1600        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1601        #[transactional]1602        pub fn add_to_contract_white_list(1603            origin,1604            contract_address: T::AccountId,1605            account_address: T::AccountId1606        ) -> DispatchResult {1607            let sender = ensure_signed(origin)?;16081609            #[cfg(feature = "runtime-benchmarks")]1610            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1611            1612            Self::ensure_contract_owned(sender, &contract_address)?;      1613            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1614            Ok(())1615        }16161617        /// Remove an address from smart contract white list.1618        /// 1619        /// # Permissions1620        /// 1621        /// * Address that deployed smart contract.1622        /// 1623        /// # Arguments1624        /// 1625        /// -`contract_address`: Address of the contract.1626        ///1627        /// -`account_address`: Address to remove.1628        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1629        #[transactional]1630        pub fn remove_from_contract_white_list(1631            origin,1632            contract_address: T::AccountId,1633            account_address: T::AccountId1634        ) -> DispatchResult {1635            let sender = ensure_signed(origin)?;16361637            #[cfg(feature = "runtime-benchmarks")]1638            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16391640            Self::ensure_contract_owned(sender, &contract_address)?;1641            <ContractWhiteList<T>>::remove(contract_address, account_address);1642            Ok(())1643        }16441645        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1646        #[transactional]1647        pub fn set_collection_limits(1648            origin,1649            collection_id: u32,1650            new_limits: CollectionLimits<T::BlockNumber>,1651        ) -> DispatchResult {1652            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1653            let mut target_collection = Self::get_collection(collection_id)?;1654            Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1655            let old_limits = &target_collection.limits;1656            let chain_limits = ChainLimit::get();16571658            // collection bounds1659            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1660                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1661                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1662                Error::<T>::CollectionLimitBoundsExceeded);16631664            // token_limit   check  prev1665            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1666            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16671668            ensure!(1669                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1670                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1671                Error::<T>::OwnerPermissionsCantBeReverted,1672            );16731674            target_collection.limits = new_limits;1675            Self::save_collection(target_collection);16761677            Ok(())1678        } 1679    }1680}16811682impl<T: Config> Module<T> {1683    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1684        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1685        Self::validate_create_item_args(&collection, &data)?;1686        Self::create_item_no_validation(&collection, owner, data)?;16871688        Ok(())1689    }16901691    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1692        // Limits check1693        Self::is_correct_transfer(target_collection, &recipient)?;16941695        // Transfer permissions check1696        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1697            Self::is_owner_or_admin_permissions(target_collection, &sender),1698            Error::<T>::NoPermission);16991700        if target_collection.access == AccessMode::WhiteList {1701            Self::check_white_list(target_collection, &sender)?;1702            Self::check_white_list(target_collection, &recipient)?;1703        }17041705        match target_collection.mode1706        {1707            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1708            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1709            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1710            _ => ()1711        };17121713        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17141715        Ok(())1716    }17171718	pub fn approve_internal(1719		sender: &T::CrossAccountId,1720		spender: &T::CrossAccountId,1721		collection: &CollectionHandle<T>,1722		item_id: TokenId,1723		amount: u1281724	) -> DispatchResult {1725		Self::token_exists(&collection, item_id)?;17261727		// Transfer permissions check1728		let bypasses_limits = collection.limits.owner_can_transfer &&1729			Self::is_owner_or_admin_permissions(1730				&collection,1731				&sender,1732			);17331734		let allowance_limit = if bypasses_limits {1735			None1736		} else if let Some(amount) = Self::owned_amount(1737			&sender,1738			&collection,1739			item_id,1740		) {1741			Some(amount)1742		} else {1743			fail!(Error::<T>::NoPermission);1744		};17451746		if collection.access == AccessMode::WhiteList {1747			Self::check_white_list(&collection, &sender)?;1748			Self::check_white_list(&collection, &spender)?;1749		}17501751		let allowance: u128 = amount1752			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1753			.ok_or(Error::<T>::NumOverflow)?;1754		if let Some(limit) = allowance_limit {1755			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1756		}1757		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17581759		if matches!(collection.mode, CollectionMode::NFT) {1760			// TODO: NFT: only one owner may exist for token in ERC7211761			collection.log(1762				Vec::from([1763					eth::APPROVAL_NFT_TOPIC,1764					eth::address_to_topic(sender.as_eth()),1765					eth::address_to_topic(spender.as_eth()),1766                    eth::u32_to_topic(item_id),1767				]),1768				abi_encode!(),1769			);1770		}17711772		if matches!(collection.mode, CollectionMode::Fungible(_)) {1773			// TODO: NFT: only one owner may exist for token in ERC201774			collection.log(1775				Vec::from([1776					eth::APPROVAL_FUNGIBLE_TOPIC,1777					eth::address_to_topic(sender.as_eth()),1778					eth::address_to_topic(spender.as_eth()),1779				]),1780				abi_encode!(uint256(allowance.into())),1781			);1782		}17831784		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1785		Ok(())1786	}17871788	pub fn transfer_from_internal(1789		sender: &T::CrossAccountId,1790		from: &T::CrossAccountId,1791		recipient: &T::CrossAccountId,1792		collection: &CollectionHandle<T>,1793		item_id: TokenId,1794		amount: u128,1795	) -> DispatchResult {1796		// Check approval1797		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17981799		// Limits check1800		Self::is_correct_transfer(&collection, &recipient)?;18011802		// Transfer permissions check1803		ensure!(1804			approval >= amount || 1805			(1806				collection.limits.owner_can_transfer &&1807				Self::is_owner_or_admin_permissions(&collection, &sender)1808			),1809			Error::<T>::NoPermission1810		);18111812		if collection.access == AccessMode::WhiteList {1813			Self::check_white_list(&collection, &sender)?;1814			Self::check_white_list(&collection, &recipient)?;1815		}18161817		// Reduce approval by transferred amount or remove if remaining approval drops to 01818		let allowance = approval.saturating_sub(amount);1819		if allowance > 0 {1820			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1821		} else {1822			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1823		}18241825		match collection.mode {1826			CollectionMode::NFT => {1827				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1828			}1829			CollectionMode::Fungible(_) => {1830				Self::transfer_fungible(&collection, amount, &from, &recipient)?1831			}1832			CollectionMode::ReFungible => {1833				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1834			}1835			_ => ()1836		};18371838		if matches!(collection.mode, CollectionMode::Fungible(_)) {1839			collection.log(1840				Vec::from([1841					eth::APPROVAL_FUNGIBLE_TOPIC,1842					eth::address_to_topic(from.as_eth()),1843					eth::address_to_topic(sender.as_eth()),1844				]),1845				abi_encode!(uint256(allowance.into())),1846			);1847		}18481849		Ok(())1850	}18511852    pub fn set_variable_meta_data_internal(1853        sender: &T::CrossAccountId,1854        collection: &CollectionHandle<T>, 1855        item_id: TokenId,1856        data: Vec<u8>,1857    ) -> DispatchResult {1858        Self::token_exists(&collection, item_id)?;18591860        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18611862        // Modify permissions check1863        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1864            Self::is_owner_or_admin_permissions(&collection, &sender),1865            Error::<T>::NoPermission);18661867        match collection.mode1868        {1869            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1870            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1871            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1872            _ => fail!(Error::<T>::UnexpectedCollectionType)1873        };18741875        Ok(())1876    }18771878    pub fn create_multiple_items_internal(1879        sender: &T::CrossAccountId,1880        collection: &CollectionHandle<T>,1881        owner: &T::CrossAccountId,1882        items_data: Vec<CreateItemData>,1883    ) -> DispatchResult {1884        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18851886        for data in &items_data {1887            Self::validate_create_item_args(&collection, data)?;1888        }1889        for data in &items_data {1890            Self::create_item_no_validation(&collection, owner, data.clone())?;1891        }18921893        Ok(())1894    }18951896    pub fn burn_item_internal(1897        sender: &T::CrossAccountId,1898        collection: &CollectionHandle<T>,1899        item_id: TokenId,1900        value: u128,1901    ) -> DispatchResult {1902        ensure!(1903            Self::is_item_owner(&sender, &collection, item_id) ||1904            (1905                collection.limits.owner_can_transfer &&1906                Self::is_owner_or_admin_permissions(&collection, &sender)1907            ),1908            Error::<T>::NoPermission1909        );19101911        if collection.access == AccessMode::WhiteList {1912            Self::check_white_list(&collection, &sender)?;1913        }19141915        match collection.mode1916        {1917            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1918            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1919            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1920            _ => ()1921        };19221923        Ok(())1924    }19251926    pub fn toggle_white_list_internal(1927        sender: &T::CrossAccountId,1928        collection: &CollectionHandle<T>,1929        address: &T::CrossAccountId,1930        whitelisted: bool,1931    ) -> DispatchResult {1932        Self::check_owner_or_admin_permissions(&collection, &sender)?;19331934        if whitelisted {1935            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1936        } else {1937            <WhiteList<T>>::remove(collection.id, address.as_sub());1938        }19391940        Ok(())1941    }19421943    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1944        let collection_id = collection.id;19451946        // check token limit and account token limit1947        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1948        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1949        1950        Ok(())1951    }19521953    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1954        let collection_id = collection.id;19551956        // check token limit and account token limit1957        let total_items: u32 = ItemListIndex::get(collection_id)1958            .checked_add(amount)1959            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1960        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1961            .checked_add(amount)1962            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1963        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1964        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19651966        if !Self::is_owner_or_admin_permissions(collection, &sender) {1967            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1968            Self::check_white_list(collection, owner)?;1969            Self::check_white_list(collection, sender)?;1970        }19711972        Ok(())1973    }19741975    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1976        match target_collection.mode1977        {1978            CollectionMode::NFT => {1979                if let CreateItemData::NFT(data) = data {1980                    // check sizes1981                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1982                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1983                } else {1984                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1985                }1986            },1987            CollectionMode::Fungible(_) => {1988                if let CreateItemData::Fungible(_) = data {1989                } else {1990                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1991                }1992            },1993            CollectionMode::ReFungible => {1994                if let CreateItemData::ReFungible(data) = data {19951996                    // check sizes1997                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1998                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19992000                    // Check refungibility limits2001                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2002                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2003                } else {2004                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2005                }2006            },2007            _ => { fail!(Error::<T>::UnexpectedCollectionType); }2008        };20092010        Ok(())2011    }20122013    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2014        match data2015        {2016            CreateItemData::NFT(data) => {2017                let item = NftItemType {2018                    owner: owner.clone(),2019                    const_data: data.const_data,2020                    variable_data: data.variable_data2021                };20222023                Self::add_nft_item(collection, item)?;2024            },2025            CreateItemData::Fungible(data) => {2026                Self::add_fungible_item(collection, &owner, data.value)?;2027            },2028            CreateItemData::ReFungible(data) => {2029                let mut owner_list = Vec::new();2030                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20312032                let item = ReFungibleItemType {2033                    owner: owner_list,2034                    const_data: data.const_data,2035                    variable_data: data.variable_data2036                };20372038                Self::add_refungible_item(collection, item)?;2039            }2040        };20412042        Ok(())2043    }20442045    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2046        let collection_id = collection.id;20472048        // Does new owner already have an account?2049        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20502051        // Mint 2052        let item = FungibleItemType {2053            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2054        };2055        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20562057        // Update balance2058        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2059            .checked_add(value)2060            .ok_or(Error::<T>::NumOverflow)?;2061        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20622063        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2064        Ok(())2065    }20662067    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2068        let collection_id = collection.id;20692070        let current_index = <ItemListIndex>::get(collection_id)2071            .checked_add(1)2072            .ok_or(Error::<T>::NumOverflow)?;2073        let itemcopy = item.clone();20742075        ensure!(2076            item.owner.len() == 1,2077            Error::<T>::BadCreateRefungibleCall,2078        );2079        let item_owner = item.owner.first().expect("only one owner is defined");20802081        let value = item_owner.fraction;2082        let owner = item_owner.owner.clone();20832084        Self::add_token_index(collection_id, current_index, &owner)?;20852086        <ItemListIndex>::insert(collection_id, current_index);2087        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20882089        // Update balance2090        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2091            .checked_add(value)2092            .ok_or(Error::<T>::NumOverflow)?;2093        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20942095        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2096        Ok(())2097    }20982099    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2100        let collection_id = collection.id;21012102        let current_index = <ItemListIndex>::get(collection_id)2103            .checked_add(1)2104            .ok_or(Error::<T>::NumOverflow)?;21052106        let item_owner = item.owner.clone();2107        Self::add_token_index(collection_id, current_index, &item.owner)?;21082109        <ItemListIndex>::insert(collection_id, current_index);2110        <NftItemList<T>>::insert(collection_id, current_index, item);21112112        // Update balance2113        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2114            .checked_add(1)2115            .ok_or(Error::<T>::NumOverflow)?;2116        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21172118        collection.log(2119            Vec::from([2120                eth::TRANSFER_NFT_TOPIC,2121                eth::address_to_topic(&H160::default()),2122                eth::address_to_topic(item_owner.as_eth()),2123                eth::u32_to_topic(current_index),2124            ]),2125            abi_encode!(),2126        );2127        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2128        Ok(())2129    }21302131    fn burn_refungible_item(2132        collection: &CollectionHandle<T>,2133        item_id: TokenId,2134        owner: &T::CrossAccountId,2135    ) -> DispatchResult {2136        let collection_id = collection.id;21372138        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2139            .ok_or(Error::<T>::TokenNotFound)?;2140        let rft_balance = token2141            .owner2142            .iter()2143            .find(|&i| i.owner == *owner)2144            .ok_or(Error::<T>::TokenNotFound)?;2145        Self::remove_token_index(collection_id, item_id, owner)?;21462147        // update balance2148        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2149            .checked_sub(rft_balance.fraction)2150            .ok_or(Error::<T>::NumOverflow)?;2151        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21522153        // Re-create owners list with sender removed2154        let index = token2155            .owner2156            .iter()2157            .position(|i| i.owner == *owner)2158            .expect("owned item is exists");2159        token.owner.remove(index);2160        let owner_count = token.owner.len();21612162        // Burn the token completely if this was the last (only) owner2163        if owner_count == 0 {2164            <ReFungibleItemList<T>>::remove(collection_id, item_id);2165            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2166        }2167        else {2168            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2169        }21702171        Ok(())2172    }21732174    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2175        let collection_id = collection.id;21762177        let item = <NftItemList<T>>::get(collection_id, item_id)2178            .ok_or(Error::<T>::TokenNotFound)?;2179        Self::remove_token_index(collection_id, item_id, &item.owner)?;21802181        // update balance2182        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2183            .checked_sub(1)2184            .ok_or(Error::<T>::NumOverflow)?;2185        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2186        <NftItemList<T>>::remove(collection_id, item_id);2187        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21882189        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2190        Ok(())2191    }21922193    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2194        let collection_id = collection.id;21952196        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2197        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21982199        // update balance2200        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2201            .checked_sub(value)2202            .ok_or(Error::<T>::NumOverflow)?;2203        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22042205        if balance.value - value > 0 {2206            balance.value -= value;2207            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2208        }2209        else {2210            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2211        }22122213        collection.log(2214            Vec::from([2215                eth::TRANSFER_FUNGIBLE_TOPIC,2216                eth::address_to_topic(owner.as_eth()),2217                eth::address_to_topic(&H160::default()),2218            ]),2219            abi_encode!(uint256(value.into())),2220        );2221        Ok(())2222    }22232224    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2225        Ok(<CollectionHandle<T>>::get(collection_id)2226            .ok_or(Error::<T>::CollectionNotFound)?)2227    }22282229    fn save_collection(collection: CollectionHandle<T>) {2230        <CollectionById<T>>::insert(collection.id, collection.into_inner());2231    }22322233    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2234        if collection.logs.is_empty() {2235            return Ok(())2236        }2237        T::EthereumTransactionSender::submit_logs_transaction(2238            eth::generate_transaction(collection.id, T::EthereumChainId::get()),2239            collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2240        )2241    }22422243    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2244        ensure!(2245            *subject == target_collection.owner,2246            Error::<T>::NoPermission2247        );22482249        Ok(())2250    }22512252    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2253        *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2254    }22552256    fn check_owner_or_admin_permissions(2257        collection: &CollectionHandle<T>,2258        subject: &T::CrossAccountId,2259    ) -> DispatchResult {2260        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22612262        Ok(())2263    }22642265    fn owned_amount(2266        subject: &T::CrossAccountId,2267        target_collection: &CollectionHandle<T>,2268        item_id: TokenId,2269    ) -> Option<u128> {2270        let collection_id = target_collection.id;22712272        match target_collection.mode {2273            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2274                .then(|| 1),2275            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2276                .value),2277            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2278                .owner2279                .iter()2280                .find(|i| i.owner == *subject)2281                .map(|i| i.fraction),2282            CollectionMode::Invalid => None,2283        }2284    }22852286    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2287        match target_collection.mode {2288            CollectionMode::Fungible(_) => true,2289            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2290        }2291    }22922293    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2294        let collection_id = collection.id;22952296        let mes = Error::<T>::AddresNotInWhiteList;2297        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22982299        Ok(())2300    }23012302    /// Check if token exists. In case of Fungible, check if there is an entry for 2303    /// the owner in fungible balances double map2304    fn token_exists(2305        target_collection: &CollectionHandle<T>,2306        item_id: TokenId,2307    ) -> DispatchResult {2308        let collection_id = target_collection.id;2309        let exists = match target_collection.mode2310        {2311            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2312            CollectionMode::Fungible(_)  => true,2313            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2314            _ => false2315        };23162317        ensure!(exists == true, Error::<T>::TokenNotFound);2318        Ok(())2319    }23202321    fn transfer_fungible(2322        collection: &CollectionHandle<T>,2323        value: u128,2324        owner: &T::CrossAccountId,2325        recipient: &T::CrossAccountId,2326    ) -> DispatchResult {2327        let collection_id = collection.id;23282329        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2330        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23312332        // Send balance to recipient (updates balanceOf of recipient)2333        Self::add_fungible_item(collection, recipient, value)?;23342335        // update balanceOf of sender2336        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23372338        // Reduce or remove sender2339        if balance.value == value {2340            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2341        }2342        else {2343            balance.value -= value;2344            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2345        }23462347        collection.log(2348            Vec::from([2349                eth::TRANSFER_FUNGIBLE_TOPIC,2350                eth::address_to_topic(owner.as_eth()),2351                eth::address_to_topic(recipient.as_eth()),2352            ]),2353            abi_encode!(uint256(value.into())),2354        );2355        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23562357        Ok(())2358    }23592360    fn transfer_refungible(2361        collection: &CollectionHandle<T>,2362        item_id: TokenId,2363        value: u128,2364        owner: T::CrossAccountId,2365        new_owner: T::CrossAccountId,2366    ) -> DispatchResult {2367        let collection_id = collection.id;2368        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2369            .ok_or(Error::<T>::TokenNotFound)?;23702371        let item = full_item2372            .owner2373            .iter()2374            .filter(|i| i.owner == owner)2375            .next()2376            .ok_or(Error::<T>::TokenNotFound)?;2377        let amount = item.fraction;23782379        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23802381        // update balance2382        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2383            .checked_sub(value)2384            .ok_or(Error::<T>::NumOverflow)?;2385        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23862387        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2388            .checked_add(value)2389            .ok_or(Error::<T>::NumOverflow)?;2390        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23912392        let old_owner = item.owner.clone();2393        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23942395        // transfer2396        if amount == value && !new_owner_has_account {2397            // change owner2398            // new owner do not have account2399            let mut new_full_item = full_item.clone();2400            new_full_item2401                .owner2402                .iter_mut()2403                .find(|i| i.owner == owner)2404                .expect("old owner does present in refungible")2405                .owner = new_owner.clone();2406            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24072408            // update index collection2409            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2410        } else {2411            let mut new_full_item = full_item.clone();2412            new_full_item2413                .owner2414                .iter_mut()2415                .find(|i| i.owner == owner)2416                .expect("old owner does present in refungible")2417                .fraction -= value;24182419            // separate amount2420            if new_owner_has_account {2421                // new owner has account2422                new_full_item2423                    .owner2424                    .iter_mut()2425                    .find(|i| i.owner == new_owner)2426                    .expect("new owner has account")2427                    .fraction += value;2428            } else {2429                // new owner do not have account2430                new_full_item.owner.push(Ownership {2431                    owner: new_owner.clone(),2432                    fraction: value,2433                });2434                Self::add_token_index(collection_id, item_id, &new_owner)?;2435            }24362437            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2438        }24392440        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24412442        Ok(())2443    }24442445    fn transfer_nft(2446        collection: &CollectionHandle<T>,2447        item_id: TokenId,2448        sender: T::CrossAccountId,2449        new_owner: T::CrossAccountId,2450    ) -> DispatchResult {2451        let collection_id = collection.id;2452        let mut item = <NftItemList<T>>::get(collection_id, item_id)2453            .ok_or(Error::<T>::TokenNotFound)?;24542455        ensure!(2456            sender == item.owner,2457            Error::<T>::MustBeTokenOwner2458        );24592460        // update balance2461        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2462            .checked_sub(1)2463            .ok_or(Error::<T>::NumOverflow)?;2464        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24652466        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2467            .checked_add(1)2468            .ok_or(Error::<T>::NumOverflow)?;2469        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24702471        // change owner2472        let old_owner = item.owner.clone();2473        item.owner = new_owner.clone();2474        <NftItemList<T>>::insert(collection_id, item_id, item);24752476        // update index collection2477        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24782479        collection.log(2480            Vec::from([2481                eth::TRANSFER_NFT_TOPIC,2482                eth::address_to_topic(sender.as_eth()),2483                eth::address_to_topic(new_owner.as_eth()),2484                eth::u32_to_topic(item_id),2485            ]),2486            abi_encode!(),2487        );2488        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24892490        Ok(())2491    }2492    2493    fn set_re_fungible_variable_data(2494        collection: &CollectionHandle<T>,2495        item_id: TokenId,2496        data: Vec<u8>2497    ) -> DispatchResult {2498        let collection_id = collection.id;2499        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2500            .ok_or(Error::<T>::TokenNotFound)?;25012502        item.variable_data = data;25032504        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25052506        Ok(())2507    }25082509    fn set_nft_variable_data(2510        collection: &CollectionHandle<T>,2511        item_id: TokenId,2512        data: Vec<u8>2513    ) -> DispatchResult {2514        let collection_id = collection.id;2515        let mut item = <NftItemList<T>>::get(collection_id, item_id)2516            .ok_or(Error::<T>::TokenNotFound)?;2517        2518        item.variable_data = data;25192520        <NftItemList<T>>::insert(collection_id, item_id, item);2521        2522        Ok(())2523    }25242525    fn init_collection(item: &Collection<T>) {2526        // check params2527        assert!(2528            item.decimal_points <= MAX_DECIMAL_POINTS,2529            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2530        );2531        assert!(2532            item.name.len() <= 64,2533            "Collection name can not be longer than 63 char"2534        );2535        assert!(2536            item.name.len() <= 256,2537            "Collection description can not be longer than 255 char"2538        );2539        assert!(2540            item.token_prefix.len() <= 16,2541            "Token prefix can not be longer than 15 char"2542        );25432544        // Generate next collection ID2545        let next_id = CreatedCollectionCount::get()2546            .checked_add(1)2547            .unwrap();25482549        CreatedCollectionCount::put(next_id);2550    }25512552    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2553        let current_index = <ItemListIndex>::get(collection_id)2554            .checked_add(1)2555            .unwrap();25562557        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25582559        <ItemListIndex>::insert(collection_id, current_index);25602561        // Update balance2562        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2563            .checked_add(1)2564            .unwrap();2565        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2566    }25672568    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2569        let current_index = <ItemListIndex>::get(collection_id)2570            .checked_add(1)2571            .unwrap();25722573        Self::add_token_index(collection_id, current_index, owner).unwrap();25742575        <ItemListIndex>::insert(collection_id, current_index);25762577        // Update balance2578        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2579            .checked_add(item.value)2580            .unwrap();2581        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2582    }25832584    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2585        let current_index = <ItemListIndex>::get(collection_id)2586            .checked_add(1)2587            .unwrap();25882589        let value = item.owner.first().unwrap().fraction;2590        let owner = item.owner.first().unwrap().owner.clone();25912592        Self::add_token_index(collection_id, current_index, &owner).unwrap();25932594        <ItemListIndex>::insert(collection_id, current_index);25952596        // Update balance2597        let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2598            .checked_add(value)2599            .unwrap();2600        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2601    }26022603    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2604        // add to account limit2605        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26062607            // bound Owned tokens by a single address2608            let count = <AccountItemCount<T>>::get(owner.as_sub());2609            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26102611            <AccountItemCount<T>>::insert(owner.as_sub(), count2612                .checked_add(1)2613                .ok_or(Error::<T>::NumOverflow)?);2614        }2615        else {2616            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2617        }26182619        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2620        if list_exists {2621            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2622            let item_contains = list.contains(&item_index.clone());26232624            if !item_contains {2625                list.push(item_index.clone());2626            }26272628            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2629        } else {2630            let mut itm = Vec::new();2631            itm.push(item_index.clone());2632            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2633        }26342635        Ok(())2636    }26372638    fn remove_token_index(2639        collection_id: CollectionId,2640        item_index: TokenId,2641        owner: &T::CrossAccountId,2642    ) -> DispatchResult {26432644        // update counter2645        <AccountItemCount<T>>::insert(owner.as_sub(), 2646            <AccountItemCount<T>>::get(owner.as_sub())2647            .checked_sub(1)2648            .ok_or(Error::<T>::NumOverflow)?);264926502651        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2652        if list_exists {2653            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2654            let item_contains = list.contains(&item_index.clone());26552656            if item_contains {2657                list.retain(|&item| item != item_index);2658                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2659            }2660        }26612662        Ok(())2663    }26642665    fn move_token_index(2666        collection_id: CollectionId,2667        item_index: TokenId,2668        old_owner: &T::CrossAccountId,2669        new_owner: &T::CrossAccountId,2670    ) -> DispatchResult {2671        Self::remove_token_index(collection_id, item_index, old_owner)?;2672        Self::add_token_index(collection_id, item_index, new_owner)?;26732674        Ok(())2675    }2676    2677    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2678        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26792680        Ok(())2681    }2682}26832684////////////////////////////////////////////////////////////////////////////////////////////////////2685// Economic models2686// #region26872688/// Fee multiplier.2689pub type Multiplier = FixedU128;26902691type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26922693/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2694/// in the queue.2695#[derive(Encode, Decode, Clone, Eq, PartialEq)]2696pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26972698impl<T: Config + Send + Sync> sp_std::fmt::Debug 2699    for ChargeTransactionPayment<T>2700{2701	#[cfg(feature = "std")]2702	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2703		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2704	}2705	#[cfg(not(feature = "std"))]2706	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2707		Ok(())2708	}2709}27102711impl<T: Config> ChargeTransactionPayment<T>2712where2713    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2714    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2715    T::AccountId: AsRef<[u8]>,2716    T::AccountId: UncheckedFrom<T::Hash>,2717{2718    fn traditional_fee(2719        len: usize,2720        info: &DispatchInfoOf<T::Call>,2721        tip: BalanceOf<T>,2722    ) -> BalanceOf<T>2723    where2724        T::Call: Dispatchable<Info = DispatchInfo>,2725    {2726        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2727    }27282729	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2730        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2731        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2732        let len_saturation = max_block_length as u64 / (len as u64).max(1);2733        let coefficient: BalanceOf<T> = weight_saturation2734            .min(len_saturation)2735            .saturated_into::<BalanceOf<T>>();2736        final_fee2737            .saturating_mul(coefficient)2738            .saturated_into::<TransactionPriority>()2739    }27402741    fn withdraw_fee(2742        &self,2743        who: &T::AccountId,2744        call: &T::Call,2745        info: &DispatchInfoOf<T::Call>,2746        len: usize,2747	) -> Result<2748		(2749			BalanceOf<T>,2750			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2751		),2752		TransactionValidityError,2753	> {2754        let tip = self.0;27552756        let fee = Self::traditional_fee(len, info, tip);27572758        // Only mess with balances if fee is not zero.2759        if fee.is_zero() {2760            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2761			.map(|i| (fee, i));2762        }27632764        // Determine who is paying transaction fee based on ecnomic model2765        // Parse call to extract collection ID and access collection sponsor2766        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2767            Some(Call::create_item(collection_id, _owner, _properties)) => {2768                let collection = <CollectionById<T>>::get(collection_id)?;27692770                // sponsor timeout2771                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27722773                let limit = collection.limits.sponsor_transfer_timeout;2774                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2775                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2776                    let limit_time = last_tx_block + limit.into();2777                    if block_number <= limit_time {2778                        return None;2779                    }2780                }2781                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27822783                // check free create limit2784                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2785                    collection.sponsorship.sponsor()2786                        .cloned()2787                } else {2788                    None2789                }2790            }2791            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2792                let collection = <CollectionById<T>>::get(collection_id)?;2793                2794                let mut sponsor_transfer = false;2795                if collection.sponsorship.confirmed() {27962797                    let collection_limits = collection.limits;2798                    let collection_mode = collection.mode;2799    2800                    // sponsor timeout2801                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2802                    sponsor_transfer = match collection_mode {2803                        CollectionMode::NFT => {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().nft_sponsor_transfer_timeout2810                            };2811    2812                            let mut sponsored = true;2813                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2814                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2815                                let limit_time = last_tx_block + limit.into();2816                                if block_number <= limit_time {2817                                    sponsored = false;2818                                }2819                            }2820                            if sponsored {2821                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2822                            }28232824                            sponsored2825                        }2826                        CollectionMode::Fungible(_) => {2827    2828                            // get correct limit2829                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2830                                collection_limits.sponsor_transfer_timeout2831                            } else {2832                                ChainLimit::get().fungible_sponsor_transfer_timeout2833                            };2834    2835                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2836                            let mut sponsored = true;2837                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2838                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2839                                let limit_time = last_tx_block + limit.into();2840                                if block_number <= limit_time {2841                                    sponsored = false;2842                                }2843                            }2844                            if sponsored {2845                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2846                            }28472848                            sponsored2849                        }2850                        CollectionMode::ReFungible => {2851    2852                            // get correct limit2853                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2854                                collection_limits.sponsor_transfer_timeout2855                            } else {2856                                ChainLimit::get().refungible_sponsor_transfer_timeout2857                            };2858    2859                            let mut sponsored = true;2860                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2861                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2862                                let limit_time = last_tx_block + limit.into();2863                                if block_number <= limit_time {2864                                    sponsored = false;2865                                }2866                            }2867                            if sponsored {2868                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2869                            }28702871                            sponsored2872                        }2873                        _ => {2874                            false2875                        },2876                    };2877                }28782879                if !sponsor_transfer {2880                    None2881                } else {2882                    collection.sponsorship.sponsor()2883                        .cloned()2884                }2885            }28862887            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2888                let mut sponsor_metadata_changes = false;28892890                let collection = <CollectionById<T>>::get(collection_id)?;28912892                if2893                    collection.sponsorship.confirmed() &&2894                    // Can't sponsor fungible collection, this tx will be rejected2895                    // as invalid2896                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2897                    data.len() <= collection.limits.sponsored_data_size as usize2898                {2899                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2900                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29012902                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2903                            .map(|last_block| block_number - last_block > rate_limit)2904                            .unwrap_or(true) 2905                        {2906                            sponsor_metadata_changes = true;2907                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2908                        }2909                    }2910                }29112912                if !sponsor_metadata_changes {2913                    None2914                } else {2915                    collection.sponsorship.sponsor().cloned()2916                }2917            }29182919            _ => None,2920        })();29212922        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2923            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29242925                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29262927                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2928                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2929                  2930                if !owned_contract && white_list_enabled {2931                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2932                        return Err(InvalidTransaction::Call.into());2933                    }2934                }2935            },2936            _ => {},2937        }29382939        // Sponsor smart contracts2940        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29412942            // On instantiation: set the contract owner2943            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29442945                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2946                    &who,2947                    code_hash,2948                    salt,2949                );2950                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29512952                None2953            },29542955            // On instantiation with code: set the contract owner2956            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {29572958                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2959                    &who,2960                    &T::Hashing::hash(&_code),2961                    _salt,2962                );29632964                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29652966                None2967            }29682969            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2970            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29712972                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29732974                let mut sponsor_transfer = false;2975                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2976                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2977                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2978                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2979                    let limit_time = last_tx_block + rate_limit;29802981                    if block_number >= limit_time {2982                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2983                        sponsor_transfer = true;2984                    }2985                } else {2986                    sponsor_transfer = false;2987                }2988               2989                if sponsor_transfer {2990                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2991                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2992                            return Some(called_contract);2993                        }2994                    }2995                }29962997                None2998            },29993000            _ => None,3001        });30023003        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30043005		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3006			.map(|i| (fee, i))3007    }3008}300930103011impl<T: Config + Send + Sync> SignedExtension3012    for ChargeTransactionPayment<T>3013where3014    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3015    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3016    T::AccountId: AsRef<[u8]>,3017    T::AccountId: UncheckedFrom<T::Hash>,3018{3019    const IDENTIFIER: &'static str = "ChargeTransactionPayment";3020    type AccountId = T::AccountId;3021    type Call = T::Call;3022    type AdditionalSigned = ();3023    type Pre = (3024        // tip3025        BalanceOf<T>,3026        // who pays fee3027        Self::AccountId,3028		// imbalance resulting from withdrawing the fee3029		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3030    );3031    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3032        Ok(())3033    }30343035    fn validate(3036        &self,3037        who: &Self::AccountId,3038        call: &Self::Call,3039        info: &DispatchInfoOf<Self::Call>,3040        len: usize,3041    ) -> TransactionValidity {3042		let (fee, _) = self.withdraw_fee(who, call, info, len)?;3043		Ok(ValidTransaction {3044			priority: Self::get_priority(len, info, fee),3045			..Default::default()3046		})3047    }30483049    fn pre_dispatch(3050        self,3051        who: &Self::AccountId,3052        call: &Self::Call,3053        info: &DispatchInfoOf<Self::Call>,3054        len: usize,3055    ) -> Result<Self::Pre, TransactionValidityError> {3056        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3057        Ok((self.0, who.clone(), imbalance))3058    }30593060    fn post_dispatch(3061        pre: Self::Pre,3062        info: &DispatchInfoOf<Self::Call>,3063        post_info: &PostDispatchInfoOf<Self::Call>,3064        len: usize,3065        _result: &DispatchResult,3066    ) -> Result<(), TransactionValidityError> {3067		let (tip, who, imbalance) = pre;3068		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3069			len as u32,3070			info,3071			post_info,3072			tip,3073		);3074		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3075		Ok(())3076    }3077}30783079// #endregion30803081sp_api::decl_runtime_apis! {3082    pub trait NftApi {3083        /// Used for ethereum integration3084        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3085    }3086}
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -45,7 +45,7 @@
       }
     },
     "Collection": {
-      "Owner": "CrossAccountId",
+      "Owner": "AccountId",
       "Mode": "CollectionMode",
       "Access": "AccessMode",
       "DecimalPoints": "DecimalPoints",
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -37,7 +37,7 @@
   }
   return input;
 }
-export function toSubstrateAddress(input: CrossAccountId): string {
+export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {
   input = normalizeAccountId(input);
   if ('substrate' in input) {
     return input.substrate;
@@ -273,7 +273,7 @@
     // tslint:disable-next-line:no-unused-expression
     expect(collection).to.be.not.null;
     expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');
-    expect(collection.Owner).to.be.deep.equal(normalizeAccountId(alicesPublicKey));
+    expect(collection.Owner).to.be.equal(toSubstrateAddress(alicesPublicKey));
     expect(utf16ToStr(collection.Name)).to.be.equal(name);
     expect(utf16ToStr(collection.Description)).to.be.equal(description);
     expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);