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

difftreelog

Testing node for forkless updates

str-mv2021-01-12parent: #31c9ea3.patch.diff
in: master

2 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 std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub collection: CollectionId,149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType<AccountId> {157    pub collection: CollectionId,158    pub owner: AccountId,159    pub value: u128,160}161162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]164pub struct ReFungibleItemType<AccountId> {165    pub collection: CollectionId,166    pub owner: Vec<Ownership<AccountId>>,167    pub const_data: Vec<u8>,168    pub variable_data: Vec<u8>,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ApprovePermissions<AccountId> {174    pub approved: AccountId,175    pub amount: u128,176}177178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct VestingItem<AccountId, Moment> {181    pub sender: AccountId,182    pub recipient: AccountId,183    pub collection_id: CollectionId,184    pub item_id: TokenId,185    pub amount: u64,186    pub vesting_date: Moment,187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct BasketItem<AccountId, BlockNumber> {192    pub address: AccountId,193    pub start_block: BlockNumber,194}195196#[derive(Encode, Decode, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct CollectionLimits {199    pub account_token_ownership_limit: u32,200    pub sponsored_data_size: u32,201    pub token_limit: u32,202203    // Timeouts for item types in passed blocks204    pub sponsor_transfer_timeout: u32,205}206207impl Default for CollectionLimits {208    fn default() -> CollectionLimits {209        CollectionLimits { 210            account_token_ownership_limit: 10_000_000, 211            token_limit: u32::max_value(),212            sponsored_data_size: u32::max_value(), 213            sponsor_transfer_timeout: 14400 }214    }215}216217#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]218#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]219pub struct ChainLimits {220    pub collection_numbers_limit: u32,221    pub account_token_ownership_limit: u32,222    pub collections_admins_limit: u64,223    pub custom_data_limit: u32,224225    // Timeouts for item types in passed blocks226    pub nft_sponsor_transfer_timeout: u32,227    pub fungible_sponsor_transfer_timeout: u32,228    pub refungible_sponsor_transfer_timeout: u32,229}230231pub trait WeightInfo {232	fn create_collection() -> Weight;233	fn destroy_collection() -> Weight;234	fn add_to_white_list() -> Weight;235	fn remove_from_white_list() -> Weight;236    fn set_public_access_mode() -> Weight;237    fn set_mint_permission() -> Weight;238    fn change_collection_owner() -> Weight;239    fn add_collection_admin() -> Weight;240    fn remove_collection_admin() -> Weight;241    fn set_collection_sponsor() -> Weight;242    fn confirm_sponsorship() -> Weight;243    fn remove_collection_sponsor() -> Weight;244    fn create_item(s: usize) -> Weight;245    fn burn_item() -> Weight;246    fn transfer() -> Weight;247    fn approve() -> Weight;248    fn transfer_from() -> Weight;249    fn set_offchain_schema() -> Weight;250    fn set_const_on_chain_schema() -> Weight;251    fn set_variable_on_chain_schema() -> Weight;252    fn set_variable_meta_data() -> Weight;253    fn enable_contract_sponsoring() -> Weight;254    fn set_schema_version() -> Weight;255    fn set_chain_limits() -> Weight;256    fn set_contract_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280}281282#[derive(Encode, Decode, Debug, Clone, PartialEq)]283#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]284pub enum CreateItemData {285    NFT(CreateNftData),286    Fungible(CreateFungibleData),287    ReFungible(CreateReFungibleData),288}289290impl CreateItemData {291    pub fn len(&self) -> usize {292        let len = match self {293            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),294            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),295            _ => 0296        };297        298        return len;299    }300}301302impl From<CreateNftData> for CreateItemData {303    fn from(item: CreateNftData) -> Self {304        CreateItemData::NFT(item)305    }306}307308impl From<CreateReFungibleData> for CreateItemData {309    fn from(item: CreateReFungibleData) -> Self {310        CreateItemData::ReFungible(item)311    }312}313314impl From<CreateFungibleData> for CreateItemData {315    fn from(item: CreateFungibleData) -> Self {316        CreateItemData::Fungible(item)317    }318}319320321decl_error! {322	/// Error for non-fungible-token module.323	pub enum Error for Module<T: Trait> {324        /// Total collections bound exceeded.325        TotalCollectionsLimitExceeded,326		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.327        CollectionDecimalPointLimitExceeded, 328        /// Collection name can not be longer than 63 char.329        CollectionNameLimitExceeded, 330        /// Collection description can not be longer than 255 char.331        CollectionDescriptionLimitExceeded, 332        /// Token prefix can not be longer than 15 char.333        CollectionTokenPrefixLimitExceeded,334        /// This collection does not exist.335        CollectionNotFound,336        /// Item not exists.337        TokenNotFound,338        /// Arithmetic calculation overflow.339        NumOverflow,       340        /// Account already has admin role.341        AlreadyAdmin,  342        /// You do not own this collection.343        NoPermission,344        /// This address is not set as sponsor, use setCollectionSponsor first.345        ConfirmUnsetSponsorFail,346        /// Collection is not in mint mode.347        PublicMintingNotAllowed,348        /// Sender parameter and item owner must be equal.349        MustBeTokenOwner,350        /// Item balance not enough.351        TokenValueTooLow,352        /// Size of item is too large.353        NftSizeLimitExceeded,354        /// No approve found355        ApproveNotFound,356        /// Requested value more than approved.357        TokenValueNotEnough,358        /// Only approved addresses can call this method.359        ApproveRequired,360        /// Address is not in white list.361        AddresNotInWhiteList,362        /// Number of collection admins bound exceeded.363        CollectionAdminsLimitExceeded,364        /// Owned tokens by a single address bound exceeded.365        AddressOwnershipLimitExceeded,366        /// Length of items properties must be greater than 0.367        EmptyArgument,368        /// const_data exceeded data limit.369        TokenConstDataLimitExceeded,370        /// variable_data exceeded data limit.371        TokenVariableDataLimitExceeded,372        /// Not NFT item data used to mint in NFT collection.373        NotNftDataUsedToMintNftCollectionToken,374        /// Not Fungible item data used to mint in Fungible collection.375        NotFungibleDataUsedToMintFungibleCollectionToken,376        /// Not Re Fungible item data used to mint in Re Fungible collection.377        NotReFungibleDataUsedToMintReFungibleCollectionToken,378        /// Unexpected collection type.379        UnexpectedCollectionType,380        /// Can't store metadata in fungible tokens.381        CantStoreMetadataInFungibleTokens,382        /// Collection token limit exceeded383        CollectionTokenLimitExceeded,384        /// Account token limit exceeded per collection385        AccountTokenLimitExceeded,386        /// Collection limit bounds per collection exceeded387        CollectionLimitBoundsExceeded388	}389}390391pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {392    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;393394    /// Weight information for extrinsics in this pallet.395	type WeightInfo: WeightInfo;396}397398#[cfg(feature = "runtime-benchmarks")]399mod benchmarking;400401// #endregion402403decl_storage! {404    trait Store for Module<T: Trait> as Nft {405406        // Private members407        NextCollectionID: CollectionId;408        CreatedCollectionCount: u32;409        ChainVersion: u64;410        ItemListIndex: map hasher(identity) CollectionId => TokenId;411412        // Chain limits struct413        pub ChainLimit get(fn chain_limit) config(): ChainLimits;414415        // Bound counters416        CollectionCount: u32;417        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;418419        // Basic collections420        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;421        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;422        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;423424        /// Balance owner per collection map425        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;426427        /// second parameter: item id + owner account id428        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;429430        /// Item collections431        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;432        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;433        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;434435        /// Index list436        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;437438        /// Tokens transfer baskets439        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;440        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;441        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442443        // Contract Sponsorship and Ownership444        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;445        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;446        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;447        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;448        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 449        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 450    }451    add_extra_genesis {452        build(|config: &GenesisConfig<T>| {453            // Modification of storage454            for (_num, _c) in &config.collection {455                <Module<T>>::init_collection(_c);456            }457458            for (_num, _q, _i) in &config.nft_item_id {459                <Module<T>>::init_nft_token(_i);460            }461462            for (_num, _q, _i) in &config.fungible_item_id {463                <Module<T>>::init_fungible_token(_i);464            }465466            for (_num, _q, _i) in &config.refungible_item_id {467                <Module<T>>::init_refungible_token(_i);468            }469        })470    }471}472473decl_event!(474    pub enum Event<T>475    where476        AccountId = <T as system::Trait>::AccountId,477    {478        /// New collection was created479        /// 480        /// # Arguments481        /// 482        /// * collection_id: Globally unique identifier of newly created collection.483        /// 484        /// * mode: [CollectionMode] converted into u8.485        /// 486        /// * account_id: Collection owner.487        Created(CollectionId, u8, AccountId),488489        /// New item was created.490        /// 491        /// # Arguments492        /// 493        /// * collection_id: Id of the collection where item was created.494        /// 495        /// * item_id: Id of an item. Unique within the collection.496        ItemCreated(CollectionId, TokenId),497498        /// Collection item was burned.499        /// 500        /// # Arguments501        /// 502        /// collection_id.503        /// 504        /// item_id: Identifier of burned NFT.505        ItemDestroyed(CollectionId, TokenId),506    }507);508509decl_module! {510    pub struct Module<T: Trait> for enum Call where origin: T::Origin {511512        fn deposit_event() = default;513        type Error = Error<T>;514515        fn on_initialize(now: T::BlockNumber) -> Weight {516517            if ChainVersion::get() < 2518            {519                let value = NextCollectionID::get();520                CreatedCollectionCount::put(value);521                ChainVersion::put(2);522            }523524            0525        }526527        /// 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.528        /// 529        /// # Permissions530        /// 531        /// * Anyone.532        /// 533        /// # Arguments534        /// 535        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.536        /// 537        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.538        /// 539        /// * token_prefix: UTF-8 string with token prefix.540        /// 541        /// * mode: [CollectionMode] collection type and type dependent data.542        // returns collection ID543        #[weight = T::WeightInfo::create_collection()]544        pub fn create_collection(origin,545                                 collection_name: Vec<u16>,546                                 collection_description: Vec<u16>,547                                 token_prefix: Vec<u8>,548                                 mode: CollectionMode) -> DispatchResult {549550            // Anyone can create a collection551            let who = ensure_signed(origin)?;552553            let decimal_points = match mode {554                CollectionMode::Fungible(points) => points,555                CollectionMode::ReFungible(points) => points,556                _ => 0557            };558559            // bound Total number of collections560            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);561562            // check params563            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);564            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);565            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);566            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);567568            // Generate next collection ID569            let next_id = CreatedCollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            // bound counter574            let total = CollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            CreatedCollectionCount::put(next_id);579            CollectionCount::put(total);580581            // Create new collection582            let new_collection = CollectionType {583                owner: who.clone(),584                name: collection_name,585                mode: mode.clone(),586                mint_mode: false,587                access: AccessMode::Normal,588                description: collection_description,589                decimal_points: decimal_points,590                token_prefix: token_prefix,591                offchain_schema: Vec::new(),592                schema_version: SchemaVersion::ImageURL,593                sponsor: T::AccountId::default(),594                unconfirmed_sponsor: T::AccountId::default(),595                variable_on_chain_schema: Vec::new(),596                const_on_chain_schema: Vec::new(),597                limits: CollectionLimits::default(),598            };599600            // Add new collection to map601            <Collection<T>>::insert(next_id, new_collection);602603            // call event604            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));605606            Ok(())607        }608609        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.610        /// 611        /// # Permissions612        /// 613        /// * Collection Owner.614        /// 615        /// # Arguments616        /// 617        /// * collection_id: collection to destroy.618        #[weight = T::WeightInfo::destroy_collection()]619        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {620621            let sender = ensure_signed(origin)?;622            Self::check_owner_permissions(collection_id, sender)?;623624            <AddressTokens<T>>::remove_prefix(collection_id);625            <ApprovedList<T>>::remove_prefix(collection_id);626            <Balance<T>>::remove_prefix(collection_id);627            <ItemListIndex>::remove(collection_id);628            <AdminList<T>>::remove(collection_id);629            <Collection<T>>::remove(collection_id);630            <WhiteList<T>>::remove_prefix(collection_id);631632            <NftItemList<T>>::remove_prefix(collection_id);633            <FungibleItemList<T>>::remove_prefix(collection_id);634            <ReFungibleItemList<T>>::remove_prefix(collection_id);635636            <NftTransferBasket<T>>::remove_prefix(collection_id);637            <FungibleTransferBasket<T>>::remove_prefix(collection_id);638            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);639640            if CollectionCount::get() > 0641            {642                // bound couter643                let total = CollectionCount::get()644                    .checked_sub(1)645                    .ok_or(Error::<T>::NumOverflow)?;646647                CollectionCount::put(total);648            }649650            Ok(())651        }652653        /// Add an address to white list.654        /// 655        /// # Permissions656        /// 657        /// * Collection Owner658        /// * Collection Admin659        /// 660        /// # Arguments661        /// 662        /// * collection_id.663        /// 664        /// * address.665        #[weight = T::WeightInfo::add_to_white_list()]666        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{667668            let sender = ensure_signed(origin)?;669            Self::check_owner_or_admin_permissions(collection_id, sender)?;670671            <WhiteList<T>>::insert(collection_id, address, true);672            673            Ok(())674        }675676        /// Remove an address from white list.677        /// 678        /// # Permissions679        /// 680        /// * Collection Owner681        /// * Collection Admin682        /// 683        /// # Arguments684        /// 685        /// * collection_id.686        /// 687        /// * address.688        #[weight = T::WeightInfo::remove_from_white_list()]689        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{690691            let sender = ensure_signed(origin)?;692            Self::check_owner_or_admin_permissions(collection_id, sender)?;693694            <WhiteList<T>>::remove(collection_id, address);695696            Ok(())697        }698699        /// Toggle between normal and white list access for the methods with access for `Anyone`.700        /// 701        /// # Permissions702        /// 703        /// * Collection Owner.704        /// 705        /// # Arguments706        /// 707        /// * collection_id.708        /// 709        /// * mode: [AccessMode]710        #[weight = T::WeightInfo::set_public_access_mode()]711        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult712        {713            let sender = ensure_signed(origin)?;714715            Self::check_owner_permissions(collection_id, sender)?;716            let mut target_collection = <Collection<T>>::get(collection_id);717            target_collection.access = mode;718            <Collection<T>>::insert(collection_id, target_collection);719720            Ok(())721        }722723        /// Allows Anyone to create tokens if:724        /// * White List is enabled, and725        /// * Address is added to white list, and726        /// * This method was called with True parameter727        /// 728        /// # Permissions729        /// * Collection Owner730        ///731        /// # Arguments732        /// 733        /// * collection_id.734        /// 735        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.736        #[weight = T::WeightInfo::set_mint_permission()]737        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult738        {739            let sender = ensure_signed(origin)?;740741            Self::check_owner_permissions(collection_id, sender)?;742            let mut target_collection = <Collection<T>>::get(collection_id);743            target_collection.mint_mode = mint_permission;744            <Collection<T>>::insert(collection_id, target_collection);745746            Ok(())747        }748749        /// Change the owner of the collection.750        /// 751        /// # Permissions752        /// 753        /// * Collection Owner.754        /// 755        /// # Arguments756        /// 757        /// * collection_id.758        /// 759        /// * new_owner.760        #[weight = T::WeightInfo::change_collection_owner()]761        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {762763            let sender = ensure_signed(origin)?;764            Self::check_owner_permissions(collection_id, sender)?;765            let mut target_collection = <Collection<T>>::get(collection_id);766            target_collection.owner = new_owner;767            <Collection<T>>::insert(collection_id, target_collection);768769            Ok(())770        }771772        /// Adds an admin of the Collection.773        /// 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. 774        /// 775        /// # Permissions776        /// 777        /// * Collection Owner.778        /// * Collection Admin.779        /// 780        /// # Arguments781        /// 782        /// * collection_id: ID of the Collection to add admin for.783        /// 784        /// * new_admin_id: Address of new admin to add.785        #[weight = T::WeightInfo::add_collection_admin()]786        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {787788            let sender = ensure_signed(origin)?;789            Self::check_owner_or_admin_permissions(collection_id, sender)?;790            let mut admin_arr: Vec<T::AccountId> = Vec::new();791792            if <AdminList<T>>::contains_key(collection_id)793            {794                admin_arr = <AdminList<T>>::get(collection_id);795                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);796            }797798            // Number of collection admins799            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);800801            admin_arr.push(new_admin_id);802            <AdminList<T>>::insert(collection_id, admin_arr);803804            Ok(())805        }806807        /// 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.808        ///809        /// # Permissions810        /// 811        /// * Collection Owner.812        /// * Collection Admin.813        /// 814        /// # Arguments815        /// 816        /// * collection_id: ID of the Collection to remove admin for.817        /// 818        /// * account_id: Address of admin to remove.819        #[weight = T::WeightInfo::remove_collection_admin()]820        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {821822            let sender = ensure_signed(origin)?;823            Self::check_owner_or_admin_permissions(collection_id, sender)?;824825            if <AdminList<T>>::contains_key(collection_id)826            {827                let mut admin_arr = <AdminList<T>>::get(collection_id);828                admin_arr.retain(|i| *i != account_id);829                <AdminList<T>>::insert(collection_id, admin_arr);830            }831832            Ok(())833        }834835        /// # Permissions836        /// 837        /// * Collection Owner838        /// 839        /// # Arguments840        /// 841        /// * collection_id.842        /// 843        /// * new_sponsor.844        #[weight = T::WeightInfo::set_collection_sponsor()]845        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {846847            let sender = ensure_signed(origin)?;848            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);849850            let mut target_collection = <Collection<T>>::get(collection_id);851            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);852853            target_collection.unconfirmed_sponsor = new_sponsor;854            <Collection<T>>::insert(collection_id, target_collection);855856            Ok(())857        }858859        /// # Permissions860        /// 861        /// * Sponsor.862        /// 863        /// # Arguments864        /// 865        /// * collection_id.866        #[weight = T::WeightInfo::confirm_sponsorship()]867        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);871872            let mut target_collection = <Collection<T>>::get(collection_id);873            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);874875            target_collection.sponsor = target_collection.unconfirmed_sponsor;876            target_collection.unconfirmed_sponsor = T::AccountId::default();877            <Collection<T>>::insert(collection_id, target_collection);878879            Ok(())880        }881882        /// Switch back to pay-per-own-transaction model.883        ///884        /// # Permissions885        ///886        /// * Collection owner.887        /// 888        /// # Arguments889        /// 890        /// * collection_id.891        #[weight = T::WeightInfo::remove_collection_sponsor()]892        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {893894            let sender = ensure_signed(origin)?;895            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);896897            let mut target_collection = <Collection<T>>::get(collection_id);898            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);899900            target_collection.sponsor = T::AccountId::default();901            <Collection<T>>::insert(collection_id, target_collection);902903            Ok(())904        }905906        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.907        /// 908        /// # Permissions909        /// 910        /// * Collection Owner.911        /// * Collection Admin.912        /// * Anyone if913        ///     * White List is enabled, and914        ///     * Address is added to white list, and915        ///     * MintPermission is enabled (see SetMintPermission method)916        /// 917        /// # Arguments918        /// 919        /// * collection_id: ID of the collection.920        /// 921        /// * owner: Address, initial owner of the NFT.922        ///923        /// * data: Token data to store on chain.924        // #[weight =925        // (130_000_000 as Weight)926        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))927        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))928        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]929930        #[weight = T::WeightInfo::create_item(data.len())]931        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {932933            let sender = ensure_signed(origin)?;934935            Self::collection_exists(collection_id)?;936937            let target_collection = <Collection<T>>::get(collection_id);938939            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;940            Self::validate_create_item_args(&target_collection, &data)?;941            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;942943            Ok(())944        }945946        /// This method creates multiple instances of NFT Collection created with CreateCollection method.947        /// 948        /// # Permissions949        /// 950        /// * Collection Owner.951        /// * Collection Admin.952        /// * Anyone if953        ///     * White List is enabled, and954        ///     * Address is added to white list, and955        ///     * MintPermission is enabled (see SetMintPermission method)956        /// 957        /// # Arguments958        /// 959        /// * collection_id: ID of the collection.960        /// 961        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].962        /// 963        /// * owner: Address, initial owner of the NFT.964        #[weight = T::WeightInfo::create_item(items_data.into_iter()965                               .map(|data| { data.len() })966                               .sum())]967        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {968969            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);970            let sender = ensure_signed(origin)?;971972            Self::collection_exists(collection_id)?;973            let target_collection = <Collection<T>>::get(collection_id);974975            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;976977            for data in &items_data {978                Self::validate_create_item_args(&target_collection, data)?;979            }980            for data in &items_data {981                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;982            }983984            Ok(())985        }986987        /// Destroys a concrete instance of NFT.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// * Current NFT Owner.994        /// 995        /// # Arguments996        /// 997        /// * collection_id: ID of the collection.998        /// 999        /// * item_id: ID of NFT to burn.1000        #[weight = T::WeightInfo::burn_item()]1001        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10021003            let sender = ensure_signed(origin)?;1004            Self::collection_exists(collection_id)?;10051006            // Transfer permissions check1007            let target_collection = <Collection<T>>::get(collection_id);1008            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1009                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1010                Error::<T>::NoPermission);10111012            if target_collection.access == AccessMode::WhiteList {1013                Self::check_white_list(collection_id, &sender)?;1014            }10151016            match target_collection.mode1017            {1018                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1019                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1020                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1021                _ => ()1022            };10231024            // call event1025            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10261027            Ok(())1028        }10291030        /// Change ownership of the token.1031        /// 1032        /// # Permissions1033        /// 1034        /// * Collection Owner1035        /// * Collection Admin1036        /// * Current NFT owner1037        ///1038        /// # Arguments1039        /// 1040        /// * recipient: Address of token recipient.1041        /// 1042        /// * collection_id.1043        /// 1044        /// * item_id: ID of the item1045        ///     * Non-Fungible Mode: Required.1046        ///     * Fungible Mode: Ignored.1047        ///     * Re-Fungible Mode: Required.1048        /// 1049        /// * value: Amount to transfer.1050        ///     * Non-Fungible Mode: Ignored1051        ///     * Fungible Mode: Must specify transferred amount1052        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1053        #[weight = T::WeightInfo::transfer()]1054        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10551056            let sender = ensure_signed(origin)?;1057            let target_collection = <Collection<T>>::get(collection_id);10581059            // Limits check1060            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10611062            // Transfer permissions check1063            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065                Error::<T>::NoPermission);10661067            if target_collection.access == AccessMode::WhiteList {1068                Self::check_white_list(collection_id, &sender)?;1069                Self::check_white_list(collection_id, &recipient)?;1070            }10711072            match target_collection.mode1073            {1074                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1075                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1076                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1077                _ => ()1078            };10791080            Ok(())1081        }10821083        /// Set, change, or remove approved address to transfer the ownership of the NFT.1084        /// 1085        /// # Permissions1086        /// 1087        /// * Collection Owner1088        /// * Collection Admin1089        /// * Current NFT owner1090        /// 1091        /// # Arguments1092        /// 1093        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1094        /// 1095        /// * collection_id.1096        /// 1097        /// * item_id: ID of the item.1098        #[weight = T::WeightInfo::approve()]1099        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11001101            let sender = ensure_signed(origin)?;11021103            // Transfer permissions check1104            let target_collection = <Collection<T>>::get(collection_id);1105            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1106                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1107                Error::<T>::NoPermission);11081109            if target_collection.access == AccessMode::WhiteList {1110                Self::check_white_list(collection_id, &sender)?;1111                Self::check_white_list(collection_id, &approved)?;1112            }11131114            // amount param stub1115            let amount = 100000000;11161117            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1118            if list_exists {11191120                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1121                let item_contains = list.iter().any(|i| i.approved == approved);11221123                if !item_contains {1124                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1125                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1126                }1127            } else {11281129                let mut list = Vec::new();1130                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1131                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1132            }11331134            Ok(())1135        }1136        1137        /// 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.1138        /// 1139        /// # Permissions1140        /// * Collection Owner1141        /// * Collection Admin1142        /// * Current NFT owner1143        /// * Address approved by current NFT owner1144        /// 1145        /// # Arguments1146        /// 1147        /// * from: Address that owns token.1148        /// 1149        /// * recipient: Address of token recipient.1150        /// 1151        /// * collection_id.1152        /// 1153        /// * item_id: ID of the item.1154        /// 1155        /// * value: Amount to transfer.1156        #[weight = T::WeightInfo::transfer_from()]1157        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11581159            let sender = ensure_signed(origin)?;1160            let mut appoved_transfer = false;11611162            // Check approve1163            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1164                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1165                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1166                if opt_item.is_some()1167                {1168                    appoved_transfer = true;1169                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1170                }1171            }11721173            let target_collection = <Collection<T>>::get(collection_id);11741175            // Limits check1176            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11771178            // Transfer permissions check         1179            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1180            Error::<T>::NoPermission);11811182            if target_collection.access == AccessMode::WhiteList {1183                Self::check_white_list(collection_id, &sender)?;1184                Self::check_white_list(collection_id, &recipient)?;1185            }11861187            // remove approve1188            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1189                .into_iter().filter(|i| i.approved != sender.clone()).collect();1190            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);119111921193            match target_collection.mode1194            {1195                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1196                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1197                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1198                _ => ()1199            };12001201            Ok(())1202        }12031204        #[weight = 0]1205        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12061207            // let no_perm_mes = "You do not have permissions to modify this collection";1208            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1209            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1210            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12111212            // // on_nft_received  call12131214            // Self::transfer(origin, collection_id, item_id, new_owner)?;12151216            Ok(())1217        }12181219        /// Set off-chain data schema.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// 1226        /// # Arguments1227        /// 1228        /// * collection_id.1229        /// 1230        /// * schema: String representing the offchain data schema.1231        #[weight = T::WeightInfo::set_variable_meta_data()]1232        pub fn set_variable_meta_data (1233            origin,1234            collection_id: CollectionId,1235            item_id: TokenId,1236            data: Vec<u8>1237        ) -> DispatchResult {1238            let sender = ensure_signed(origin)?;1239            1240            Self::collection_exists(collection_id)?;1241            1242            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12431244            // Modify permissions check1245            let target_collection = <Collection<T>>::get(collection_id);1246            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1247                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1248                Error::<T>::NoPermission);12491250            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12511252            match target_collection.mode1253            {1254                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1255                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1256                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1257                _ => fail!(Error::<T>::UnexpectedCollectionType)1258            };12591260            Ok(())1261        }1262 1263        /// Set schema standard1264        /// ImageURL1265        /// Unique1266        /// 1267        /// # Permissions1268        /// 1269        /// * Collection Owner1270        /// * Collection Admin1271        /// 1272        /// # Arguments1273        /// 1274        /// * collection_id.1275        /// 1276        /// * schema: SchemaVersion: enum1277        #[weight = T::WeightInfo::set_schema_version()]1278        pub fn set_schema_version(1279            origin,1280            collection_id: CollectionId,1281            version: SchemaVersion1282        ) -> DispatchResult {1283            let sender = ensure_signed(origin)?;1284            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1285            let mut target_collection = <Collection<T>>::get(collection_id);1286            target_collection.schema_version = version;1287            <Collection<T>>::insert(collection_id, target_collection);12881289            Ok(())1290        }12911292        /// Set off-chain data schema.1293        /// 1294        /// # Permissions1295        /// 1296        /// * Collection Owner1297        /// * Collection Admin1298        /// 1299        /// # Arguments1300        /// 1301        /// * collection_id.1302        /// 1303        /// * schema: String representing the offchain data schema.1304        #[weight = T::WeightInfo::set_offchain_schema()]1305        pub fn set_offchain_schema(1306            origin,1307            collection_id: CollectionId,1308            schema: Vec<u8>1309        ) -> DispatchResult {1310            let sender = ensure_signed(origin)?;1311            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13121313            let mut target_collection = <Collection<T>>::get(collection_id);1314            target_collection.offchain_schema = schema;1315            <Collection<T>>::insert(collection_id, target_collection);13161317            Ok(())1318        }13191320        /// Set const on-chain data schema.1321        /// 1322        /// # Permissions1323        /// 1324        /// * Collection Owner1325        /// * Collection Admin1326        /// 1327        /// # Arguments1328        /// 1329        /// * collection_id.1330        /// 1331        /// * schema: String representing the const on-chain data schema.1332        #[weight = T::WeightInfo::set_const_on_chain_schema()]1333        pub fn set_const_on_chain_schema (1334            origin,1335            collection_id: CollectionId,1336            schema: Vec<u8>1337        ) -> DispatchResult {1338            let sender = ensure_signed(origin)?;1339            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13401341            let mut target_collection = <Collection<T>>::get(collection_id);1342            target_collection.const_on_chain_schema = schema;1343            <Collection<T>>::insert(collection_id, target_collection);13441345            Ok(())1346        }13471348        /// Set variable on-chain data schema.1349        /// 1350        /// # Permissions1351        /// 1352        /// * Collection Owner1353        /// * Collection Admin1354        /// 1355        /// # Arguments1356        /// 1357        /// * collection_id.1358        /// 1359        /// * schema: String representing the variable on-chain data schema.1360        #[weight = T::WeightInfo::set_const_on_chain_schema()]1361        pub fn set_variable_on_chain_schema (1362            origin,1363            collection_id: CollectionId,1364            schema: Vec<u8>1365        ) -> DispatchResult {1366            let sender = ensure_signed(origin)?;1367            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13681369            let mut target_collection = <Collection<T>>::get(collection_id);1370            target_collection.variable_on_chain_schema = schema;1371            <Collection<T>>::insert(collection_id, target_collection);13721373            Ok(())1374        }13751376        // Sudo permissions function1377        #[weight = T::WeightInfo::set_chain_limits()]1378        pub fn set_chain_limits(1379            origin,1380            limits: ChainLimits1381        ) -> DispatchResult {13821383            #[cfg(not(feature = "runtime-benchmarks"))]1384            ensure_root(origin)?;13851386            <ChainLimit>::put(limits);1387            Ok(())1388        }13891390        /// Enable smart contract self-sponsoring.1391        /// 1392        /// # Permissions1393        /// 1394        /// * Contract Owner1395        /// 1396        /// # Arguments1397        /// 1398        /// * contract address1399        /// * enable flag1400        /// 1401        #[weight = T::WeightInfo::enable_contract_sponsoring()]1402        pub fn enable_contract_sponsoring(1403            origin,1404            contract_address: T::AccountId,1405            enable: bool1406        ) -> DispatchResult {14071408            let sender = ensure_signed(origin)?;14091410            #[cfg(feature = "runtime-benchmarks")]1411            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14121413            Self::ensure_contract_owned(sender, &contract_address)?;14141415            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1416            Ok(())1417        }14181419        /// Set the rate limit for contract sponsoring to specified number of blocks.1420        /// 1421        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1422        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1423        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1424        /// from contract endowment if there are at least B blocks between such transactions. 1425        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1426        /// 1427        /// # Permissions1428        /// 1429        /// * Contract Owner1430        /// 1431        /// # Arguments1432        /// 1433        /// -`contract_address`: Address of the contract to sponsor1434        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1435        /// 1436        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1437        pub fn set_contract_sponsoring_rate_limit(1438            origin,1439            contract_address: T::AccountId,1440            rate_limit: T::BlockNumber1441        ) -> DispatchResult {1442            let sender = ensure_signed(origin)?;14431444            #[cfg(feature = "runtime-benchmarks")]1445            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14461447            Self::ensure_contract_owned(sender, &contract_address)?;1448            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1449            Ok(())1450        }14511452        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1453        /// 1454        /// # Permissions1455        /// 1456        /// * Address that deployed smart contract.1457        /// 1458        /// # Arguments1459        /// 1460        /// -`contract_address`: Address of the contract.1461        /// 1462        /// - `enable`: .  1463        #[weight = T::WeightInfo::toggle_contract_white_list()]1464        pub fn toggle_contract_white_list(1465            origin,1466            contract_address: T::AccountId,1467            enable: bool1468        ) -> DispatchResult {1469            let sender = ensure_signed(origin)?;14701471            #[cfg(feature = "runtime-benchmarks")]1472            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14731474            Self::ensure_contract_owned(sender, &contract_address)?;1475            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1476            Ok(())1477        }1478        1479        /// Add an address to smart contract white list.1480        /// 1481        /// # Permissions1482        /// 1483        /// * Address that deployed smart contract.1484        /// 1485        /// # Arguments1486        /// 1487        /// -`contract_address`: Address of the contract.1488        ///1489        /// -`account_address`: Address to add.1490        #[weight = T::WeightInfo::add_to_contract_white_list()]1491        pub fn add_to_contract_white_list(1492            origin,1493            contract_address: T::AccountId,1494            account_address: T::AccountId1495        ) -> DispatchResult {1496            let sender = ensure_signed(origin)?;14971498            #[cfg(feature = "runtime-benchmarks")]1499            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1500            1501            Self::ensure_contract_owned(sender, &contract_address)?;      1502            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1503            Ok(())1504        }15051506        /// Remove an address from smart contract white list.1507        /// 1508        /// # Permissions1509        /// 1510        /// * Address that deployed smart contract.1511        /// 1512        /// # Arguments1513        /// 1514        /// -`contract_address`: Address of the contract.1515        ///1516        /// -`account_address`: Address to remove.1517        #[weight = T::WeightInfo::remove_from_contract_white_list()]1518        pub fn remove_from_contract_white_list(1519            origin,1520            contract_address: T::AccountId,1521            account_address: T::AccountId1522        ) -> DispatchResult {1523            let sender = ensure_signed(origin)?;15241525            #[cfg(feature = "runtime-benchmarks")]1526            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15271528            Self::ensure_contract_owned(sender, &contract_address)?;1529            <ContractWhiteList<T>>::remove(contract_address, account_address);1530            Ok(())1531        }15321533        #[weight = T::WeightInfo::set_collection_limits()]1534        pub fn set_collection_limits(1535            origin,1536            collection_id: u32,1537            limits: CollectionLimits,1538        ) -> DispatchResult {1539            let sender = ensure_signed(origin)?;1540            Self::check_owner_permissions(collection_id, sender.clone())?;1541            let mut target_collection = <Collection<T>>::get(collection_id);1542            let chain_limits = ChainLimit::get();1543            let climits = target_collection.limits;15441545            // collection bounds1546            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1547                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1548                Error::<T>::CollectionLimitBoundsExceeded);15491550            // token_limit   check  prev1551            ensure!(climits.token_limit > limits.token_limit && 1552                limits.token_limit <= chain_limits.account_token_ownership_limit, 1553                Error::<T>::AccountTokenLimitExceeded);15541555            target_collection.limits = limits;1556            <Collection<T>>::insert(collection_id, target_collection);15571558            Ok(())1559        } 1560    }1561}15621563impl<T: Trait> Module<T> {15641565    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15661567        // check token limit and account token limit1568        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1569        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1570        1571        Ok(())1572    }15731574    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15751576        // check token limit and account token limit1577        let total_items: u32 = ItemListIndex::get(collection_id);1578        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1579        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1580        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15811582        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1583            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1584            Self::check_white_list(collection_id, owner)?;1585            Self::check_white_list(collection_id, sender)?;1586        }15871588        Ok(())1589    }15901591    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1592        match target_collection.mode1593        {1594            CollectionMode::NFT => {1595                if let CreateItemData::NFT(data) = data {1596                    // check sizes1597                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1598                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1599                } else {1600                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1601                }1602            },1603            CollectionMode::Fungible(_) => {1604                if let CreateItemData::Fungible(_) = data {1605                } else {1606                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1607                }1608            },1609            CollectionMode::ReFungible(_) => {1610                if let CreateItemData::ReFungible(data) = data {16111612                    // check sizes1613                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1614                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1615                } else {1616                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1617                }1618            },1619            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1620        };16211622        Ok(())1623    }16241625    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1626        match data1627        {1628            CreateItemData::NFT(data) => {1629                let item = NftItemType {1630                    collection: collection_id,1631                    owner,1632                    const_data: data.const_data,1633                    variable_data: data.variable_data1634                };16351636                Self::add_nft_item(item)?;1637            },1638            CreateItemData::Fungible(_) => {1639                let item = FungibleItemType {1640                    collection: collection_id,1641                    owner,1642                    value: (10 as u128).pow(collection.decimal_points as u32)1643                };16441645                Self::add_fungible_item(item)?;1646            },1647            CreateItemData::ReFungible(data) => {1648                let mut owner_list = Vec::new();1649                let value = (10 as u128).pow(collection.decimal_points as u32);1650                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16511652                let item = ReFungibleItemType {1653                    collection: collection_id,1654                    owner: owner_list,1655                    const_data: data.const_data,1656                    variable_data: data.variable_data1657                };16581659                Self::add_refungible_item(item)?;1660            }1661        };16621663        // call event1664        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16651666        Ok(())1667    }16681669    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1670        let current_index = <ItemListIndex>::get(item.collection)1671            .checked_add(1)1672            .ok_or(Error::<T>::NumOverflow)?;1673        let itemcopy = item.clone();1674        let owner = item.owner.clone();16751676        Self::add_token_index(item.collection, current_index, owner.clone())?;16771678        <ItemListIndex>::insert(item.collection, current_index);1679        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16801681        // Add current block1682        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1683        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1684        1685        // Update balance1686        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1687            .checked_add(item.value)1688            .ok_or(Error::<T>::NumOverflow)?;1689        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16901691        Ok(())1692    }16931694    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1695        let current_index = <ItemListIndex>::get(item.collection)1696            .checked_add(1)1697            .ok_or(Error::<T>::NumOverflow)?;1698        let itemcopy = item.clone();16991700        let value = item.owner.first().unwrap().fraction;1701        let owner = item.owner.first().unwrap().owner.clone();17021703        Self::add_token_index(item.collection, current_index, owner.clone())?;17041705        <ItemListIndex>::insert(item.collection, current_index);1706        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);17071708        // Add current block1709        let block_number: T::BlockNumber = 0.into();1710        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);17111712        // Update balance1713        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1714            .checked_add(value)1715            .ok_or(Error::<T>::NumOverflow)?;1716        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);17171718        Ok(())1719    }17201721    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1722        let current_index = <ItemListIndex>::get(item.collection)1723            .checked_add(1)1724            .ok_or(Error::<T>::NumOverflow)?;17251726        let item_owner = item.owner.clone();1727        let collection_id = item.collection.clone();1728        Self::add_token_index(collection_id, current_index, item.owner.clone())?;17291730        <ItemListIndex>::insert(collection_id, current_index);1731        <NftItemList<T>>::insert(collection_id, current_index, item);17321733        // Add current block1734        let block_number: T::BlockNumber = 0.into();1735        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);17361737        // Update balance1738        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1739            .checked_add(1)1740            .ok_or(Error::<T>::NumOverflow)?;1741        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17421743        Ok(())1744    }17451746    fn burn_refungible_item(1747        collection_id: CollectionId,1748        item_id: TokenId,1749        owner: T::AccountId,1750    ) -> DispatchResult {1751        ensure!(1752            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1753            Error::<T>::TokenNotFound1754        );1755        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1756        let item = collection1757            .owner1758            .iter()1759            .filter(|&i| i.owner == owner)1760            .next()1761            .unwrap();1762        Self::remove_token_index(collection_id, item_id, owner.clone())?;17631764        // remove approve list1765        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17661767        // update balance1768        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1769            .checked_sub(item.fraction)1770            .ok_or(Error::<T>::NumOverflow)?;1771        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17721773        <ReFungibleItemList<T>>::remove(collection_id, item_id);17741775        Ok(())1776    }17771778    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1779        ensure!(1780            <NftItemList<T>>::contains_key(collection_id, item_id),1781            Error::<T>::TokenNotFound1782        );1783        let item = <NftItemList<T>>::get(collection_id, item_id);1784        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17851786        // remove approve list1787        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17881789        // update balance1790        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1791            .checked_sub(1)1792            .ok_or(Error::<T>::NumOverflow)?;1793        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1794        <NftItemList<T>>::remove(collection_id, item_id);17951796        Ok(())1797    }17981799    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1800        ensure!(1801            <FungibleItemList<T>>::contains_key(collection_id, item_id),1802            Error::<T>::TokenNotFound1803        );1804        let item = <FungibleItemList<T>>::get(collection_id, item_id);1805        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;18061807        // remove approve list1808        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));18091810        // update balance1811        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1812            .checked_sub(item.value)1813            .ok_or(Error::<T>::NumOverflow)?;1814        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);18151816        <FungibleItemList<T>>::remove(collection_id, item_id);18171818        Ok(())1819    }18201821    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1822        ensure!(1823            <Collection<T>>::contains_key(collection_id),1824            Error::<T>::CollectionNotFound1825        );1826        Ok(())1827    }18281829    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1830        Self::collection_exists(collection_id)?;18311832        let target_collection = <Collection<T>>::get(collection_id);1833        ensure!(1834            subject == target_collection.owner,1835            Error::<T>::NoPermission1836        );18371838        Ok(())1839    }18401841    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1842        let target_collection = <Collection<T>>::get(collection_id);1843        let mut result: bool = subject == target_collection.owner;1844        let exists = <AdminList<T>>::contains_key(collection_id);18451846        if !result & exists {1847            if <AdminList<T>>::get(collection_id).contains(&subject) {1848                result = true1849            }1850        }18511852        result1853    }18541855    fn check_owner_or_admin_permissions(1856        collection_id: CollectionId,1857        subject: T::AccountId,1858    ) -> DispatchResult {1859        Self::collection_exists(collection_id)?;1860        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18611862        ensure!(1863            result,1864            Error::<T>::NoPermission1865        );1866        Ok(())1867    }18681869    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1870        let target_collection = <Collection<T>>::get(collection_id);18711872        match target_collection.mode {1873            CollectionMode::NFT => {1874                <NftItemList<T>>::get(collection_id, item_id).owner == subject1875            }1876            CollectionMode::Fungible(_) => {1877                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1878            }1879            CollectionMode::ReFungible(_) => {1880                <ReFungibleItemList<T>>::get(collection_id, item_id)1881                    .owner1882                    .iter()1883                    .any(|i| i.owner == subject)1884            }1885            CollectionMode::Invalid => false,1886        }1887    }18881889    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1890        let mes = Error::<T>::AddresNotInWhiteList;1891        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18921893        Ok(())1894    }18951896    fn transfer_fungible(1897        collection_id: CollectionId,1898        item_id: TokenId,1899        value: u128,1900        owner: T::AccountId,1901        new_owner: T::AccountId,1902    ) -> DispatchResult {1903        ensure!(1904            <FungibleItemList<T>>::contains_key(collection_id, item_id),1905            Error::<T>::TokenNotFound1906        );19071908        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1909        let amount = full_item.value;19101911        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19121913        // update balance1914        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1915            .checked_sub(value)1916            .ok_or(Error::<T>::NumOverflow)?;1917        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);19181919        let mut new_owner_account_id = 0;1920        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1921        if new_owner_items.len() > 0 {1922            new_owner_account_id = new_owner_items[0];1923        }19241925        // transfer1926        if amount == value && new_owner_account_id == 0 {1927            // change owner1928            // new owner do not have account1929            let mut new_full_item = full_item.clone();1930            new_full_item.owner = new_owner.clone();1931            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19321933            // update balance1934            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1935                .checked_add(value)1936                .ok_or(Error::<T>::NumOverflow)?;1937            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19381939            // update index collection1940            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1941        } else {1942            let mut new_full_item = full_item.clone();1943            new_full_item.value -= value;19441945            // separate amount1946            if new_owner_account_id > 0 {1947                // new owner has account1948                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1949                item.value += value;19501951                // update balance1952                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1953                    .checked_add(value)1954                    .ok_or(Error::<T>::NumOverflow)?;1955                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19561957                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1958            } else {1959                // new owner do not have account1960                let item = FungibleItemType {1961                    collection: collection_id,1962                    owner: new_owner.clone(),1963                    value1964                };19651966                Self::add_fungible_item(item)?;1967            }19681969            if amount == value {1970                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19711972                // remove approve list1973                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1974                <FungibleItemList<T>>::remove(collection_id, item_id);1975            }19761977            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1978        }19791980        Ok(())1981    }19821983    fn transfer_refungible(1984        collection_id: CollectionId,1985        item_id: TokenId,1986        value: u128,1987        owner: T::AccountId,1988        new_owner: T::AccountId,1989    ) -> DispatchResult {1990        ensure!(1991            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1992            Error::<T>::TokenNotFound1993        );19941995        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1996        let item = full_item1997            .owner1998            .iter()1999            .filter(|i| i.owner == owner)2000            .next()2001            .ok_or(Error::<T>::NumOverflow)?;2002        let amount = item.fraction;20032004        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20052006        // update balance2007        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2008            .checked_sub(value)2009            .ok_or(Error::<T>::NumOverflow)?;2010        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20112012        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2013            .checked_add(value)2014            .ok_or(Error::<T>::NumOverflow)?;2015        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20162017        let old_owner = item.owner.clone();2018        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20192020        // transfer2021        if amount == value && !new_owner_has_account {2022            // change owner2023            // new owner do not have account2024            let mut new_full_item = full_item.clone();2025            new_full_item2026                .owner2027                .iter_mut()2028                .find(|i| i.owner == owner)2029                .unwrap()2030                .owner = new_owner.clone();2031            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20322033            // update index collection2034            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;2035        } else {2036            let mut new_full_item = full_item.clone();2037            new_full_item2038                .owner2039                .iter_mut()2040                .find(|i| i.owner == owner)2041                .unwrap()2042                .fraction -= value;20432044            // separate amount2045            if new_owner_has_account {2046                // new owner has account2047                new_full_item2048                    .owner2049                    .iter_mut()2050                    .find(|i| i.owner == new_owner)2051                    .unwrap()2052                    .fraction += value;2053            } else {2054                // new owner do not have account2055                new_full_item.owner.push(Ownership {2056                    owner: new_owner.clone(),2057                    fraction: value,2058                });2059                Self::add_token_index(collection_id, item_id, new_owner.clone())?;2060            }20612062            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2063        }20642065        Ok(())2066    }20672068    fn transfer_nft(2069        collection_id: CollectionId,2070        item_id: TokenId,2071        sender: T::AccountId,2072        new_owner: T::AccountId,2073    ) -> DispatchResult {2074        ensure!(2075            <NftItemList<T>>::contains_key(collection_id, item_id),2076            Error::<T>::TokenNotFound2077        );20782079        let mut item = <NftItemList<T>>::get(collection_id, item_id);20802081        ensure!(2082            sender == item.owner,2083            Error::<T>::MustBeTokenOwner2084        );20852086        // update balance2087        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2088            .checked_sub(1)2089            .ok_or(Error::<T>::NumOverflow)?;2090        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20912092        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2093            .checked_add(1)2094            .ok_or(Error::<T>::NumOverflow)?;2095        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20962097        // change owner2098        let old_owner = item.owner.clone();2099        item.owner = new_owner.clone();2100        <NftItemList<T>>::insert(collection_id, item_id, item);21012102        // update index collection2103        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;21042105        // reset approved list2106        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2107        Ok(())2108    }2109    2110    fn item_exists(2111        collection_id: CollectionId,2112        item_id: TokenId,2113        mode: &CollectionMode2114    ) -> DispatchResult {2115        match mode {2116            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2117            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2118            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2119            _ => ()2120        };2121        2122        Ok(())2123    }21242125    fn set_re_fungible_variable_data(2126        collection_id: CollectionId,2127        item_id: TokenId,2128        data: Vec<u8>2129    ) -> DispatchResult {2130        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21312132        item.variable_data = data;21332134        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21352136        Ok(())2137    }21382139    fn set_nft_variable_data(2140        collection_id: CollectionId,2141        item_id: TokenId,2142        data: Vec<u8>2143    ) -> DispatchResult {2144        let mut item = <NftItemList<T>>::get(collection_id, item_id);2145        2146        item.variable_data = data;21472148        <NftItemList<T>>::insert(collection_id, item_id, item);2149        2150        Ok(())2151    }21522153    fn init_collection(item: &CollectionType<T::AccountId>) {2154        // check params2155        assert!(2156            item.decimal_points <= MAX_DECIMAL_POINTS,2157            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2158        );2159        assert!(2160            item.name.len() <= 64,2161            "Collection name can not be longer than 63 char"2162        );2163        assert!(2164            item.name.len() <= 256,2165            "Collection description can not be longer than 255 char"2166        );2167        assert!(2168            item.token_prefix.len() <= 16,2169            "Token prefix can not be longer than 15 char"2170        );21712172        // Generate next collection ID2173        let next_id = CreatedCollectionCount::get()2174            .checked_add(1)2175            .unwrap();21762177        CreatedCollectionCount::put(next_id);2178    }21792180    fn init_nft_token(item: &NftItemType<T::AccountId>) {2181        let current_index = <ItemListIndex>::get(item.collection)2182            .checked_add(1)2183            .unwrap();21842185        let item_owner = item.owner.clone();2186        let collection_id = item.collection.clone();2187        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21882189        <ItemListIndex>::insert(collection_id, current_index);21902191        // Update balance2192        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2193            .checked_add(1)2194            .unwrap();2195        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2196    }21972198    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2199        let current_index = <ItemListIndex>::get(item.collection)2200            .checked_add(1)2201            .unwrap();2202        let owner = item.owner.clone();22032204        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22052206        <ItemListIndex>::insert(item.collection, current_index);22072208        // Update balance2209        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2210            .checked_add(item.value)2211            .unwrap();2212        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2213    }22142215    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2216        let current_index = <ItemListIndex>::get(item.collection)2217            .checked_add(1)2218            .unwrap();22192220        let value = item.owner.first().unwrap().fraction;2221        let owner = item.owner.first().unwrap().owner.clone();22222223        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22242225        <ItemListIndex>::insert(item.collection, current_index);22262227        // Update balance2228        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2229            .checked_add(value)2230            .unwrap();2231        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2232    }22332234    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {22352236        // add to account limit2237        if <AccountItemCount<T>>::contains_key(owner.clone()) {22382239            // bound Owned tokens by a single address2240            let count = <AccountItemCount<T>>::get(owner.clone());2241            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22422243            <AccountItemCount<T>>::insert(owner.clone(), count2244                .checked_add(1)2245                .ok_or(Error::<T>::NumOverflow)?);2246        }2247        else {2248            <AccountItemCount<T>>::insert(owner.clone(), 1);2249        }22502251        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2252        if list_exists {2253            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2254            let item_contains = list.contains(&item_index.clone());22552256            if !item_contains {2257                list.push(item_index.clone());2258            }22592260            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2261        } else {2262            let mut itm = Vec::new();2263            itm.push(item_index.clone());2264            <AddressTokens<T>>::insert(collection_id, owner, itm);2265            2266        }22672268        Ok(())2269    }22702271    fn remove_token_index(2272        collection_id: CollectionId,2273        item_index: TokenId,2274        owner: T::AccountId,2275    ) -> DispatchResult {22762277        // update counter2278        <AccountItemCount<T>>::insert(owner.clone(), 2279            <AccountItemCount<T>>::get(owner.clone())2280            .checked_sub(1)2281            .ok_or(Error::<T>::NumOverflow)?);228222832284        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2285        if list_exists {2286            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2287            let item_contains = list.contains(&item_index.clone());22882289            if item_contains {2290                list.retain(|&item| item != item_index);2291                <AddressTokens<T>>::insert(collection_id, owner, list);2292            }2293        }22942295        Ok(())2296    }22972298    fn move_token_index(2299        collection_id: CollectionId,2300        item_index: TokenId,2301        old_owner: T::AccountId,2302        new_owner: T::AccountId,2303    ) -> DispatchResult {2304        Self::remove_token_index(collection_id, item_index, old_owner)?;2305        Self::add_token_index(collection_id, item_index, new_owner)?;23062307        Ok(())2308    }2309    2310    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2311        if <ContractOwner<T>>::contains_key(contract.clone()) {2312            let owner = <ContractOwner<T>>::get(contract);2313            ensure!(account == owner, Error::<T>::NoPermission);2314        } else {2315            fail!(Error::<T>::NoPermission);2316        }23172318        Ok(())2319    }2320}23212322////////////////////////////////////////////////////////////////////////////////////////////////////2323// Economic models2324// #region23252326/// Fee multiplier.2327pub type Multiplier = FixedU128;23282329type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2330    <T as system::Trait>::AccountId,2331>>::Balance;2332type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2333    <T as system::Trait>::AccountId,2334>>::NegativeImbalance;23352336/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2337/// in the queue.2338#[derive(Encode, Decode, Clone, Eq, PartialEq)]2339pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2340    #[codec(compact)] BalanceOf<T>2341);23422343impl<T: Trait + Send + Sync> sp_std::fmt::Debug2344    for ChargeTransactionPayment<T>2345{2346    #[cfg(feature = "std")]2347    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2348        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2349    }2350    #[cfg(not(feature = "std"))]2351    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2352        Ok(())2353    }2354}23552356impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2357where2358    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2359    BalanceOf<T>: Send + Sync + FixedPointOperand,2360{2361    /// utility constructor. Used only in client/factory code.2362    pub fn from(fee: BalanceOf<T>) -> Self {2363        Self(fee)2364    }23652366    pub fn traditional_fee(2367        len: usize,2368        info: &DispatchInfoOf<T::Call>,2369        tip: BalanceOf<T>,2370    ) -> BalanceOf<T>2371    where2372        T::Call: Dispatchable<Info = DispatchInfo>,2373    {2374        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2375    }23762377	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2378		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2379		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2380		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2381		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2382	}23832384    fn withdraw_fee(2385        &self,2386        who: &T::AccountId,2387        call: &T::Call,2388        info: &DispatchInfoOf<T::Call>,2389        len: usize,2390    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2391        let tip = self.0;23922393        // Set fee based on call type. Creating collection costs 1 Unique.2394        // All other transactions have traditional fees so far2395        // let fee = match call.is_sub_type() {2396        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2397        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2398        //                                                 // _ => <BalanceOf<T>>::from(100)2399        // };2400        let fee = Self::traditional_fee(len, info, tip);24012402        // Determine who is paying transaction fee based on ecnomic model2403        // Parse call to extract collection ID and access collection sponsor2404        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2405            Some(Call::create_item(collection_id, _owner, _properties)) => {24062407                // check free create limit2408                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2409                {2410                    <Collection<T>>::get(collection_id).sponsor2411                } else {2412                    T::AccountId::default()2413                }2414            }2415            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2416                2417                let _collection_limits = <Collection<T>>::get(collection_id).limits;2418                let _collection_mode = <Collection<T>>::get(collection_id).mode;24192420                // sponsor timeout2421                let sponsor_transfer = match _collection_mode {2422                    CollectionMode::NFT => {24232424                        // get correct limit2425                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2426                            _collection_limits.sponsor_transfer_timeout2427                        } else {2428                            ChainLimit::get().nft_sponsor_transfer_timeout2429                        };24302431                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2432                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2433                        let limit_time = basket + limit.into();2434                        if block_number >= limit_time {2435                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2436                            true2437                        }2438                        else {2439                            false2440                        }2441                    }2442                    CollectionMode::Fungible(_) => {24432444                        // get correct limit2445                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2446                            _collection_limits.sponsor_transfer_timeout2447                        } else {2448                            ChainLimit::get().fungible_sponsor_transfer_timeout2449                        };24502451                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2452                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2453                        if basket.iter().any(|i| i.address == _new_owner.clone())2454                        {2455                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2456                            let limit_time = item.start_block + limit.into();2457                            if block_number >= limit_time {2458                                basket.retain(|x| x.address == item.address);2459                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2460                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2461                                true2462                            }2463                            else {2464                                false2465                            }2466                        }2467                        else {2468                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2469                            true2470                        }2471                    }2472                    CollectionMode::ReFungible(_) => {24732474                        // get correct limit2475                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2476                            _collection_limits.sponsor_transfer_timeout2477                        } else {2478                            ChainLimit::get().refungible_sponsor_transfer_timeout2479                        };24802481                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2482                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2483                        let limit_time = basket + limit.into();2484                        if block_number >= limit_time {2485                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2486                            true2487                        } else {2488                            false2489                        }2490                    }2491                    _ => {2492                        false2493                    },2494                };24952496                if !sponsor_transfer {2497                    T::AccountId::default()2498                } else {2499                    <Collection<T>>::get(collection_id).sponsor2500                }2501            }25022503            _ => T::AccountId::default(),2504        };25052506        // Sponsor smart contracts2507        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25082509            // On instantiation: set the contract owner2510            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {25112512                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2513                    code_hash,2514                    &data,2515                    &who,2516                );2517                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25182519                T::AccountId::default()2520            },25212522            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2523            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25242525                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25262527                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2528                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2529                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2530                  2531                if !owned_contract && white_list_enabled {2532                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2533                        return Err(InvalidTransaction::Call.into());2534                    }2535                }25362537                let mut sponsor_transfer = false;2538                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2539                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2540                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2541                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2542                    let limit_time = last_tx_block + rate_limit;25432544                    if block_number >= limit_time {2545                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2546                        sponsor_transfer = true;2547                    }2548                } else {2549                    sponsor_transfer = false;2550                }2551               2552                2553                let mut sp = T::AccountId::default();2554                if sponsor_transfer {2555                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2556                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2557                            sp = called_contract;2558                        }2559                    }2560                }25612562                sp2563            },25642565            _ => sponsor,2566        };25672568        let mut who_pays_fee: T::AccountId = sponsor.clone();2569        if sponsor == T::AccountId::default() {2570            who_pays_fee = who.clone();2571        }25722573        // Only mess with balances if fee is not zero.2574        if fee.is_zero() {2575            return Ok((fee, None));2576        }25772578        match <T as transaction_payment::Trait>::Currency::withdraw(2579            &who_pays_fee,2580            fee,2581            if tip.is_zero() {2582                WithdrawReason::TransactionPayment.into()2583            } else {2584                WithdrawReason::TransactionPayment | WithdrawReason::Tip2585            },2586            ExistenceRequirement::KeepAlive,2587        ) {2588            Ok(imbalance) => Ok((fee, Some(imbalance))),2589            Err(_) => Err(InvalidTransaction::Payment.into()),2590        }2591    }2592}259325942595impl<T: Trait + Send + Sync> SignedExtension2596    for ChargeTransactionPayment<T>2597where2598    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2599    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2600{2601    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2602    type AccountId = T::AccountId;2603    type Call = T::Call;2604    type AdditionalSigned = ();2605    type Pre = (2606        BalanceOf<T>,2607        Self::AccountId,2608        Option<NegativeImbalanceOf<T>>,2609        BalanceOf<T>,2610    );2611    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2612        Ok(())2613    }26142615    fn validate(2616        &self,2617        who: &Self::AccountId,2618        call: &Self::Call,2619        info: &DispatchInfoOf<Self::Call>,2620        len: usize,2621    ) -> TransactionValidity {2622		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2623		Ok(ValidTransaction {2624			priority: Self::get_priority(len, info, fee),2625			..Default::default()2626		})2627    }26282629    fn pre_dispatch(2630        self,2631        who: &Self::AccountId,2632        call: &Self::Call,2633        info: &DispatchInfoOf<Self::Call>,2634        len: usize,2635    ) -> Result<Self::Pre, TransactionValidityError> {2636        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2637        Ok((self.0, who.clone(), imbalance, fee))2638    }26392640    fn post_dispatch(2641        pre: Self::Pre,2642        info: &DispatchInfoOf<Self::Call>,2643        post_info: &PostDispatchInfoOf<Self::Call>,2644        len: usize,2645        _result: &DispatchResult,2646    ) -> Result<(), TransactionValidityError> {2647        let (tip, who, imbalance, fee) = pre;2648        if let Some(payed) = imbalance {2649            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2650                len as u32, info, post_info, tip,2651            );2652            let refund = fee.saturating_sub(actual_fee);2653            let actual_payment =2654                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2655                    &who, refund,2656                ) {2657                    Ok(refund_imbalance) => {2658                        // The refund cannot be larger than the up front payed max weight.2659                        // `PostDispatchInfo::calc_unspent` guards against such a case.2660                        match payed.offset(refund_imbalance) {2661                            Ok(actual_payment) => actual_payment,2662                            Err(_) => return Err(InvalidTransaction::Payment.into()),2663                        }2664                    }2665                    // We do not recreate the account using the refund. The up front payment2666                    // is gone in that case.2667                    Err(_) => payed,2668                };2669            let imbalances = actual_payment.split(tip);2670            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2671                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2672            );2673        }2674        Ok(())2675    }2676}26772678// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub collection: CollectionId,149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType<AccountId> {157    pub collection: CollectionId,158    pub owner: AccountId,159    pub value: u128,160}161162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]164pub struct ReFungibleItemType<AccountId> {165    pub collection: CollectionId,166    pub owner: Vec<Ownership<AccountId>>,167    pub const_data: Vec<u8>,168    pub variable_data: Vec<u8>,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ApprovePermissions<AccountId> {174    pub approved: AccountId,175    pub amount: u128,176}177178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct VestingItem<AccountId, Moment> {181    pub sender: AccountId,182    pub recipient: AccountId,183    pub collection_id: CollectionId,184    pub item_id: TokenId,185    pub amount: u64,186    pub vesting_date: Moment,187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct BasketItem<AccountId, BlockNumber> {192    pub address: AccountId,193    pub start_block: BlockNumber,194}195196#[derive(Encode, Decode, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct CollectionLimits {199    pub account_token_ownership_limit: u32,200    pub sponsored_data_size: u32,201    pub token_limit: u32,202203    // Timeouts for item types in passed blocks204    pub sponsor_transfer_timeout: u32,205}206207impl Default for CollectionLimits {208    fn default() -> CollectionLimits {209        CollectionLimits { 210            account_token_ownership_limit: 10_000_000, 211            token_limit: u32::max_value(),212            sponsored_data_size: u32::max_value(), 213            sponsor_transfer_timeout: 14400 }214    }215}216217#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]218#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]219pub struct ChainLimits {220    pub collection_numbers_limit: u32,221    pub account_token_ownership_limit: u32,222    pub collections_admins_limit: u64,223    pub custom_data_limit: u32,224225    // Timeouts for item types in passed blocks226    pub nft_sponsor_transfer_timeout: u32,227    pub fungible_sponsor_transfer_timeout: u32,228    pub refungible_sponsor_transfer_timeout: u32,229}230231pub trait WeightInfo {232	fn create_collection() -> Weight;233	fn destroy_collection() -> Weight;234	fn add_to_white_list() -> Weight;235	fn remove_from_white_list() -> Weight;236    fn set_public_access_mode() -> Weight;237    fn set_mint_permission() -> Weight;238    fn change_collection_owner() -> Weight;239    fn add_collection_admin() -> Weight;240    fn remove_collection_admin() -> Weight;241    fn set_collection_sponsor() -> Weight;242    fn confirm_sponsorship() -> Weight;243    fn remove_collection_sponsor() -> Weight;244    fn create_item(s: usize) -> Weight;245    fn burn_item() -> Weight;246    fn transfer() -> Weight;247    fn approve() -> Weight;248    fn transfer_from() -> Weight;249    fn set_offchain_schema() -> Weight;250    fn set_const_on_chain_schema() -> Weight;251    fn set_variable_on_chain_schema() -> Weight;252    fn set_variable_meta_data() -> Weight;253    fn enable_contract_sponsoring() -> Weight;254    fn set_schema_version() -> Weight;255    fn set_chain_limits() -> Weight;256    fn set_contract_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280}281282#[derive(Encode, Decode, Debug, Clone, PartialEq)]283#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]284pub enum CreateItemData {285    NFT(CreateNftData),286    Fungible(CreateFungibleData),287    ReFungible(CreateReFungibleData),288}289290impl CreateItemData {291    pub fn len(&self) -> usize {292        let len = match self {293            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),294            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),295            _ => 0296        };297        298        return len;299    }300}301302impl From<CreateNftData> for CreateItemData {303    fn from(item: CreateNftData) -> Self {304        CreateItemData::NFT(item)305    }306}307308impl From<CreateReFungibleData> for CreateItemData {309    fn from(item: CreateReFungibleData) -> Self {310        CreateItemData::ReFungible(item)311    }312}313314impl From<CreateFungibleData> for CreateItemData {315    fn from(item: CreateFungibleData) -> Self {316        CreateItemData::Fungible(item)317    }318}319320321decl_error! {322	/// Error for non-fungible-token module.323	pub enum Error for Module<T: Trait> {324        /// Total collections bound exceeded.325        TotalCollectionsLimitExceeded,326		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.327        CollectionDecimalPointLimitExceeded, 328        /// Collection name can not be longer than 63 char.329        CollectionNameLimitExceeded, 330        /// Collection description can not be longer than 255 char.331        CollectionDescriptionLimitExceeded, 332        /// Token prefix can not be longer than 15 char.333        CollectionTokenPrefixLimitExceeded,334        /// This collection does not exist.335        CollectionNotFound,336        /// Item not exists.337        TokenNotFound,338        /// Arithmetic calculation overflow.339        NumOverflow,       340        /// Account already has admin role.341        AlreadyAdmin,  342        /// You do not own this collection.343        NoPermission,344        /// This address is not set as sponsor, use setCollectionSponsor first.345        ConfirmUnsetSponsorFail,346        /// Collection is not in mint mode.347        PublicMintingNotAllowed,348        /// Sender parameter and item owner must be equal.349        MustBeTokenOwner,350        /// Item balance not enough.351        TokenValueTooLow,352        /// Size of item is too large.353        NftSizeLimitExceeded,354        /// No approve found355        ApproveNotFound,356        /// Requested value more than approved.357        TokenValueNotEnough,358        /// Only approved addresses can call this method.359        ApproveRequired,360        /// Address is not in white list.361        AddresNotInWhiteList,362        /// Number of collection admins bound exceeded.363        CollectionAdminsLimitExceeded,364        /// Owned tokens by a single address bound exceeded.365        AddressOwnershipLimitExceeded,366        /// Length of items properties must be greater than 0.367        EmptyArgument,368        /// const_data exceeded data limit.369        TokenConstDataLimitExceeded,370        /// variable_data exceeded data limit.371        TokenVariableDataLimitExceeded,372        /// Not NFT item data used to mint in NFT collection.373        NotNftDataUsedToMintNftCollectionToken,374        /// Not Fungible item data used to mint in Fungible collection.375        NotFungibleDataUsedToMintFungibleCollectionToken,376        /// Not Re Fungible item data used to mint in Re Fungible collection.377        NotReFungibleDataUsedToMintReFungibleCollectionToken,378        /// Unexpected collection type.379        UnexpectedCollectionType,380        /// Can't store metadata in fungible tokens.381        CantStoreMetadataInFungibleTokens,382        /// Collection token limit exceeded383        CollectionTokenLimitExceeded,384        /// Account token limit exceeded per collection385        AccountTokenLimitExceeded,386        /// Collection limit bounds per collection exceeded387        CollectionLimitBoundsExceeded388	}389}390391pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {392    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;393394    /// Weight information for extrinsics in this pallet.395	type WeightInfo: WeightInfo;396}397398#[cfg(feature = "runtime-benchmarks")]399mod benchmarking;400401// #endregion402403decl_storage! {404    trait Store for Module<T: Trait> as Nft {405406        // Private members407        NextCollectionID: CollectionId;408        CreatedCollectionCount: u32;409        ChainVersion: u64;410        ItemListIndex: map hasher(identity) CollectionId => TokenId;411412        // Chain limits struct413        pub ChainLimit get(fn chain_limit) config(): ChainLimits;414415        // Bound counters416        CollectionCount: u32;417        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;418419        // Basic collections420        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;421        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;422        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;423424        /// Balance owner per collection map425        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;426427        /// second parameter: item id + owner account id428        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;429430        /// Item collections431        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;432        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;433        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;434435        /// Index list436        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;437438        /// Tokens transfer baskets439        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;440        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;441        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442443        // Contract Sponsorship and Ownership444        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;445        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;446        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;447        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;448        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 449        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 450    }451    add_extra_genesis {452        build(|config: &GenesisConfig<T>| {453            // Modification of storage454            for (_num, _c) in &config.collection {455                <Module<T>>::init_collection(_c);456            }457458            for (_num, _q, _i) in &config.nft_item_id {459                <Module<T>>::init_nft_token(_i);460            }461462            for (_num, _q, _i) in &config.fungible_item_id {463                <Module<T>>::init_fungible_token(_i);464            }465466            for (_num, _q, _i) in &config.refungible_item_id {467                <Module<T>>::init_refungible_token(_i);468            }469        })470    }471}472473decl_event!(474    pub enum Event<T>475    where476        AccountId = <T as system::Trait>::AccountId,477    {478        /// New collection was created479        /// 480        /// # Arguments481        /// 482        /// * collection_id: Globally unique identifier of newly created collection.483        /// 484        /// * mode: [CollectionMode] converted into u8.485        /// 486        /// * account_id: Collection owner.487        Created(CollectionId, u8, AccountId),488489        /// New item was created.490        /// 491        /// # Arguments492        /// 493        /// * collection_id: Id of the collection where item was created.494        /// 495        /// * item_id: Id of an item. Unique within the collection.496        ItemCreated(CollectionId, TokenId),497498        /// Collection item was burned.499        /// 500        /// # Arguments501        /// 502        /// collection_id.503        /// 504        /// item_id: Identifier of burned NFT.505        ItemDestroyed(CollectionId, TokenId),506    }507);508509decl_module! {510    pub struct Module<T: Trait> for enum Call where origin: T::Origin {511512        fn deposit_event() = default;513        type Error = Error<T>;514515        fn on_initialize(now: T::BlockNumber) -> Weight {516517            if ChainVersion::get() < 2518            {519                let value = NextCollectionID::get();520                CreatedCollectionCount::put(value);521                ChainVersion::put(2);522            }523524            0525        }526527        /// 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.528        /// 529        /// # Permissions530        /// 531        /// * Anyone.532        /// 533        /// # Arguments534        /// 535        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.536        /// 537        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.538        /// 539        /// * token_prefix: UTF-8 string with token prefix.540        /// 541        /// * mode: [CollectionMode] collection type and type dependent data.542        // returns collection ID543        #[weight = T::WeightInfo::create_collection()]544        pub fn create_collection(origin,545                                 collection_name: Vec<u16>,546                                 collection_description: Vec<u16>,547                                 token_prefix: Vec<u8>,548                                 mode: CollectionMode) -> DispatchResult {549550            // Anyone can create a collection551            let who = ensure_signed(origin)?;552553            let decimal_points = match mode {554                CollectionMode::Fungible(points) => points,555                CollectionMode::ReFungible(points) => points,556                _ => 0557            };558559            // bound Total number of collections560            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);561562            // check params563            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);564            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);565            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);566            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);567568            // Generate next collection ID569            let next_id = CreatedCollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            // bound counter574            let total = CollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            CreatedCollectionCount::put(next_id);579            CollectionCount::put(total);580581            // Create new collection582            let new_collection = CollectionType {583                owner: who.clone(),584                name: collection_name,585                mode: mode.clone(),586                mint_mode: false,587                access: AccessMode::Normal,588                description: collection_description,589                decimal_points: decimal_points,590                token_prefix: token_prefix,591                offchain_schema: Vec::new(),592                schema_version: SchemaVersion::ImageURL,593                sponsor: T::AccountId::default(),594                unconfirmed_sponsor: T::AccountId::default(),595                variable_on_chain_schema: Vec::new(),596                const_on_chain_schema: Vec::new(),597                limits: CollectionLimits::default(),598            };599600            // Add new collection to map601            <Collection<T>>::insert(next_id, new_collection);602603            // call event604            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));605606            Ok(())607        }608609        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.610        /// 611        /// # Permissions612        /// 613        /// * Collection Owner.614        /// 615        /// # Arguments616        /// 617        /// * collection_id: collection to destroy.618        #[weight = T::WeightInfo::destroy_collection()]619        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {620621            let sender = ensure_signed(origin)?;622            Self::check_owner_permissions(collection_id, sender)?;623624            <AddressTokens<T>>::remove_prefix(collection_id);625            <ApprovedList<T>>::remove_prefix(collection_id);626            <Balance<T>>::remove_prefix(collection_id);627            <ItemListIndex>::remove(collection_id);628            <AdminList<T>>::remove(collection_id);629            <Collection<T>>::remove(collection_id);630            <WhiteList<T>>::remove_prefix(collection_id);631632            <NftItemList<T>>::remove_prefix(collection_id);633            <FungibleItemList<T>>::remove_prefix(collection_id);634            <ReFungibleItemList<T>>::remove_prefix(collection_id);635636            <NftTransferBasket<T>>::remove_prefix(collection_id);637            <FungibleTransferBasket<T>>::remove_prefix(collection_id);638            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);639640            if CollectionCount::get() > 0641            {642                // bound couter643                let total = CollectionCount::get()644                    .checked_sub(1)645                    .ok_or(Error::<T>::NumOverflow)?;646647                CollectionCount::put(total);648            }649650            Ok(())651        }652653        /// Add an address to white list.654        /// 655        /// # Permissions656        /// 657        /// * Collection Owner658        /// * Collection Admin659        /// 660        /// # Arguments661        /// 662        /// * collection_id.663        /// 664        /// * address.665        #[weight = T::WeightInfo::add_to_white_list()]666        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{667668            let sender = ensure_signed(origin)?;669            Self::check_owner_or_admin_permissions(collection_id, sender)?;670671            <WhiteList<T>>::insert(collection_id, address, true);672            673            Ok(())674        }675676        /// Remove an address from white list.677        /// 678        /// # Permissions679        /// 680        /// * Collection Owner681        /// * Collection Admin682        /// 683        /// # Arguments684        /// 685        /// * collection_id.686        /// 687        /// * address.688        #[weight = T::WeightInfo::remove_from_white_list()]689        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{690691            let sender = ensure_signed(origin)?;692            Self::check_owner_or_admin_permissions(collection_id, sender)?;693694            <WhiteList<T>>::remove(collection_id, address);695696            Ok(())697        }698699        /// Toggle between normal and white list access for the methods with access for `Anyone`.700        /// 701        /// # Permissions702        /// 703        /// * Collection Owner.704        /// 705        /// # Arguments706        /// 707        /// * collection_id.708        /// 709        /// * mode: [AccessMode]710        #[weight = T::WeightInfo::set_public_access_mode()]711        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult712        {713            let sender = ensure_signed(origin)?;714715            Self::check_owner_permissions(collection_id, sender)?;716            let mut target_collection = <Collection<T>>::get(collection_id);717            target_collection.access = mode;718            <Collection<T>>::insert(collection_id, target_collection);719720            Ok(())721        }722723        /// Allows Anyone to create tokens if:724        /// * White List is enabled, and725        /// * Address is added to white list, and726        /// * This method was called with True parameter727        /// 728        /// # Permissions729        /// * Collection Owner730        ///731        /// # Arguments732        /// 733        /// * collection_id.734        /// 735        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.736        #[weight = T::WeightInfo::set_mint_permission()]737        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult738        {739            let sender = ensure_signed(origin)?;740741            Self::check_owner_permissions(collection_id, sender)?;742            let mut target_collection = <Collection<T>>::get(collection_id);743            target_collection.mint_mode = mint_permission;744            <Collection<T>>::insert(collection_id, target_collection);745746            Ok(())747        }748749        /// Change the owner of the collection.750        /// 751        /// # Permissions752        /// 753        /// * Collection Owner.754        /// 755        /// # Arguments756        /// 757        /// * collection_id.758        /// 759        /// * new_owner.760        #[weight = T::WeightInfo::change_collection_owner()]761        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {762763            let sender = ensure_signed(origin)?;764            Self::check_owner_permissions(collection_id, sender)?;765            let mut target_collection = <Collection<T>>::get(collection_id);766            target_collection.owner = new_owner;767            <Collection<T>>::insert(collection_id, target_collection);768769            Ok(())770        }771772        /// Adds an admin of the Collection.773        /// 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. 774        /// 775        /// # Permissions776        /// 777        /// * Collection Owner.778        /// * Collection Admin.779        /// 780        /// # Arguments781        /// 782        /// * collection_id: ID of the Collection to add admin for.783        /// 784        /// * new_admin_id: Address of new admin to add.785        #[weight = T::WeightInfo::add_collection_admin()]786        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {787788            let sender = ensure_signed(origin)?;789            Self::check_owner_or_admin_permissions(collection_id, sender)?;790            let mut admin_arr: Vec<T::AccountId> = Vec::new();791792            if <AdminList<T>>::contains_key(collection_id)793            {794                admin_arr = <AdminList<T>>::get(collection_id);795                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);796            }797798            // Number of collection admins799            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);800801            admin_arr.push(new_admin_id);802            <AdminList<T>>::insert(collection_id, admin_arr);803804            Ok(())805        }806807        /// 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.808        ///809        /// # Permissions810        /// 811        /// * Collection Owner.812        /// * Collection Admin.813        /// 814        /// # Arguments815        /// 816        /// * collection_id: ID of the Collection to remove admin for.817        /// 818        /// * account_id: Address of admin to remove.819        #[weight = T::WeightInfo::remove_collection_admin()]820        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {821822            let sender = ensure_signed(origin)?;823            Self::check_owner_or_admin_permissions(collection_id, sender)?;824825            if <AdminList<T>>::contains_key(collection_id)826            {827                let mut admin_arr = <AdminList<T>>::get(collection_id);828                admin_arr.retain(|i| *i != account_id);829                <AdminList<T>>::insert(collection_id, admin_arr);830            }831832            Ok(())833        }834835        /// # Permissions836        /// 837        /// * Collection Owner838        /// 839        /// # Arguments840        /// 841        /// * collection_id.842        /// 843        /// * new_sponsor.844        #[weight = T::WeightInfo::set_collection_sponsor()]845        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {846847            let sender = ensure_signed(origin)?;848            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);849850            let mut target_collection = <Collection<T>>::get(collection_id);851            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);852853            target_collection.unconfirmed_sponsor = new_sponsor;854            <Collection<T>>::insert(collection_id, target_collection);855856            Ok(())857        }858859        /// # Permissions860        /// 861        /// * Sponsor.862        /// 863        /// # Arguments864        /// 865        /// * collection_id.866        #[weight = T::WeightInfo::confirm_sponsorship()]867        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);871872            let mut target_collection = <Collection<T>>::get(collection_id);873            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);874875            target_collection.sponsor = target_collection.unconfirmed_sponsor;876            target_collection.unconfirmed_sponsor = T::AccountId::default();877            <Collection<T>>::insert(collection_id, target_collection);878879            Ok(())880        }881882        /// Switch back to pay-per-own-transaction model.883        ///884        /// # Permissions885        ///886        /// * Collection owner.887        /// 888        /// # Arguments889        /// 890        /// * collection_id.891        #[weight = T::WeightInfo::remove_collection_sponsor()]892        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {893894            let sender = ensure_signed(origin)?;895            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);896897            let mut target_collection = <Collection<T>>::get(collection_id);898            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);899900            target_collection.sponsor = T::AccountId::default();901            <Collection<T>>::insert(collection_id, target_collection);902903            Ok(())904        }905906        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.907        /// 908        /// # Permissions909        /// 910        /// * Collection Owner.911        /// * Collection Admin.912        /// * Anyone if913        ///     * White List is enabled, and914        ///     * Address is added to white list, and915        ///     * MintPermission is enabled (see SetMintPermission method)916        /// 917        /// # Arguments918        /// 919        /// * collection_id: ID of the collection.920        /// 921        /// * owner: Address, initial owner of the NFT.922        ///923        /// * data: Token data to store on chain.924        // #[weight =925        // (130_000_000 as Weight)926        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))927        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))928        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]929930        #[weight = T::WeightInfo::create_item(data.len())]931        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {932933            let sender = ensure_signed(origin)?;934935            Self::collection_exists(collection_id)?;936937            let target_collection = <Collection<T>>::get(collection_id);938939            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;940            Self::validate_create_item_args(&target_collection, &data)?;941            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;942943            Ok(())944        }945946        /// This method creates multiple instances of NFT Collection created with CreateCollection method.947        /// 948        /// # Permissions949        /// 950        /// * Collection Owner.951        /// * Collection Admin.952        /// * Anyone if953        ///     * White List is enabled, and954        ///     * Address is added to white list, and955        ///     * MintPermission is enabled (see SetMintPermission method)956        /// 957        /// # Arguments958        /// 959        /// * collection_id: ID of the collection.960        /// 961        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].962        /// 963        /// * owner: Address, initial owner of the NFT.964        #[weight = T::WeightInfo::create_item(items_data.into_iter()965                               .map(|data| { data.len() })966                               .sum())]967        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {968969            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);970            let sender = ensure_signed(origin)?;971972            Self::collection_exists(collection_id)?;973            let target_collection = <Collection<T>>::get(collection_id);974975            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;976977            for data in &items_data {978                Self::validate_create_item_args(&target_collection, data)?;979            }980            for data in &items_data {981                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;982            }983984            Ok(())985        }986987        /// Destroys a concrete instance of NFT.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// * Current NFT Owner.994        /// 995        /// # Arguments996        /// 997        /// * collection_id: ID of the collection.998        /// 999        /// * item_id: ID of NFT to burn.1000        #[weight = T::WeightInfo::burn_item()]1001        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10021003            let sender = ensure_signed(origin)?;1004            Self::collection_exists(collection_id)?;10051006            // Transfer permissions check1007            let target_collection = <Collection<T>>::get(collection_id);1008            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1009                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1010                Error::<T>::NoPermission);10111012            if target_collection.access == AccessMode::WhiteList {1013                Self::check_white_list(collection_id, &sender)?;1014            }10151016            match target_collection.mode1017            {1018                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1019                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1020                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1021                _ => ()1022            };10231024            // call event1025            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10261027            Ok(())1028        }10291030        /// Change ownership of the token.1031        /// 1032        /// # Permissions1033        /// 1034        /// * Collection Owner1035        /// * Collection Admin1036        /// * Current NFT owner1037        ///1038        /// # Arguments1039        /// 1040        /// * recipient: Address of token recipient.1041        /// 1042        /// * collection_id.1043        /// 1044        /// * item_id: ID of the item1045        ///     * Non-Fungible Mode: Required.1046        ///     * Fungible Mode: Ignored.1047        ///     * Re-Fungible Mode: Required.1048        /// 1049        /// * value: Amount to transfer.1050        ///     * Non-Fungible Mode: Ignored1051        ///     * Fungible Mode: Must specify transferred amount1052        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1053        #[weight = T::WeightInfo::transfer()]1054        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10551056            let sender = ensure_signed(origin)?;1057            let target_collection = <Collection<T>>::get(collection_id);10581059            // Limits check1060            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10611062            // Transfer permissions check1063            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065                Error::<T>::NoPermission);10661067            if target_collection.access == AccessMode::WhiteList {1068                Self::check_white_list(collection_id, &sender)?;1069                Self::check_white_list(collection_id, &recipient)?;1070            }10711072            match target_collection.mode1073            {1074                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1075                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1076                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1077                _ => ()1078            };10791080            Ok(())1081        }10821083        /// Set, change, or remove approved address to transfer the ownership of the NFT.1084        /// 1085        /// # Permissions1086        /// 1087        /// * Collection Owner1088        /// * Collection Admin1089        /// * Current NFT owner1090        /// 1091        /// # Arguments1092        /// 1093        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1094        /// 1095        /// * collection_id.1096        /// 1097        /// * item_id: ID of the item.1098        #[weight = T::WeightInfo::approve()]1099        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11001101            let sender = ensure_signed(origin)?;11021103            // Transfer permissions check1104            let target_collection = <Collection<T>>::get(collection_id);1105            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1106                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1107                Error::<T>::NoPermission);11081109            if target_collection.access == AccessMode::WhiteList {1110                Self::check_white_list(collection_id, &sender)?;1111                Self::check_white_list(collection_id, &approved)?;1112            }11131114            // amount param stub1115            let amount = 100000000;11161117            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1118            if list_exists {11191120                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1121                let item_contains = list.iter().any(|i| i.approved == approved);11221123                if !item_contains {1124                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1125                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1126                }1127            } else {11281129                let mut list = Vec::new();1130                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1131                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1132            }11331134            Ok(())1135        }1136        1137        /// 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.1138        /// 1139        /// # Permissions1140        /// * Collection Owner1141        /// * Collection Admin1142        /// * Current NFT owner1143        /// * Address approved by current NFT owner1144        /// 1145        /// # Arguments1146        /// 1147        /// * from: Address that owns token.1148        /// 1149        /// * recipient: Address of token recipient.1150        /// 1151        /// * collection_id.1152        /// 1153        /// * item_id: ID of the item.1154        /// 1155        /// * value: Amount to transfer.1156        #[weight = T::WeightInfo::transfer_from()]1157        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11581159            let sender = ensure_signed(origin)?;1160            let mut appoved_transfer = false;11611162            // Check approve1163            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1164                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1165                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1166                if opt_item.is_some()1167                {1168                    appoved_transfer = true;1169                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1170                }1171            }11721173            let target_collection = <Collection<T>>::get(collection_id);11741175            // Limits check1176            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11771178            // Transfer permissions check         1179            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1180            Error::<T>::NoPermission);11811182            if target_collection.access == AccessMode::WhiteList {1183                Self::check_white_list(collection_id, &sender)?;1184                Self::check_white_list(collection_id, &recipient)?;1185            }11861187            // remove approve1188            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1189                .into_iter().filter(|i| i.approved != sender.clone()).collect();1190            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);119111921193            match target_collection.mode1194            {1195                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1196                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1197                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1198                _ => ()1199            };12001201            Ok(())1202        }12031204        #[weight = 0]1205        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12061207            // let no_perm_mes = "You do not have permissions to modify this collection";1208            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1209            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1210            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12111212            // // on_nft_received  call12131214            // Self::transfer(origin, collection_id, item_id, new_owner)?;12151216            Ok(())1217        }12181219        /// Set off-chain data schema.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// 1226        /// # Arguments1227        /// 1228        /// * collection_id.1229        /// 1230        /// * schema: String representing the offchain data schema.1231        #[weight = T::WeightInfo::set_variable_meta_data()]1232        pub fn set_variable_meta_data (1233            origin,1234            collection_id: CollectionId,1235            item_id: TokenId,1236            data: Vec<u8>1237        ) -> DispatchResult {1238            let sender = ensure_signed(origin)?;1239            1240            Self::collection_exists(collection_id)?;1241            1242            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12431244            // Modify permissions check1245            let target_collection = <Collection<T>>::get(collection_id);1246            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1247                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1248                Error::<T>::NoPermission);12491250            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12511252            match target_collection.mode1253            {1254                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1255                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1256                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1257                _ => fail!(Error::<T>::UnexpectedCollectionType)1258            };12591260            Ok(())1261        }1262 1263        /// Set schema standard1264        /// ImageURL1265        /// Unique1266        /// 1267        /// # Permissions1268        /// 1269        /// * Collection Owner1270        /// * Collection Admin1271        /// 1272        /// # Arguments1273        /// 1274        /// * collection_id.1275        /// 1276        /// * schema: SchemaVersion: enum1277        #[weight = T::WeightInfo::set_schema_version()]1278        pub fn set_schema_version(1279            origin,1280            collection_id: CollectionId,1281            version: SchemaVersion1282        ) -> DispatchResult {1283            let sender = ensure_signed(origin)?;1284            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1285            let mut target_collection = <Collection<T>>::get(collection_id);1286            target_collection.schema_version = version;1287            <Collection<T>>::insert(collection_id, target_collection);12881289            Ok(())1290        }12911292        /// Set off-chain data schema.1293        /// 1294        /// # Permissions1295        /// 1296        /// * Collection Owner1297        /// * Collection Admin1298        /// 1299        /// # Arguments1300        /// 1301        /// * collection_id.1302        /// 1303        /// * schema: String representing the offchain data schema.1304        #[weight = T::WeightInfo::set_offchain_schema()]1305        pub fn set_offchain_schema(1306            origin,1307            collection_id: CollectionId,1308            schema: Vec<u8>1309        ) -> DispatchResult {1310            let sender = ensure_signed(origin)?;1311            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13121313            let mut target_collection = <Collection<T>>::get(collection_id);1314            target_collection.offchain_schema = schema;1315            <Collection<T>>::insert(collection_id, target_collection);13161317            Ok(())1318        }13191320        /// Set const on-chain data schema.1321        /// 1322        /// # Permissions1323        /// 1324        /// * Collection Owner1325        /// * Collection Admin1326        /// 1327        /// # Arguments1328        /// 1329        /// * collection_id.1330        /// 1331        /// * schema: String representing the const on-chain data schema.1332        #[weight = T::WeightInfo::set_const_on_chain_schema()]1333        pub fn set_const_on_chain_schema (1334            origin,1335            collection_id: CollectionId,1336            schema: Vec<u8>1337        ) -> DispatchResult {1338            let sender = ensure_signed(origin)?;1339            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13401341            let mut target_collection = <Collection<T>>::get(collection_id);1342            target_collection.const_on_chain_schema = schema;1343            <Collection<T>>::insert(collection_id, target_collection);13441345            Ok(())1346        }13471348        /// Set variable on-chain data schema.1349        /// 1350        /// # Permissions1351        /// 1352        /// * Collection Owner1353        /// * Collection Admin1354        /// 1355        /// # Arguments1356        /// 1357        /// * collection_id.1358        /// 1359        /// * schema: String representing the variable on-chain data schema.1360        #[weight = T::WeightInfo::set_const_on_chain_schema()]1361        pub fn set_variable_on_chain_schema (1362            origin,1363            collection_id: CollectionId,1364            schema: Vec<u8>1365        ) -> DispatchResult {1366            let sender = ensure_signed(origin)?;1367            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13681369            let mut target_collection = <Collection<T>>::get(collection_id);1370            target_collection.variable_on_chain_schema = schema;1371            <Collection<T>>::insert(collection_id, target_collection);13721373            Ok(())1374        }13751376        // Sudo permissions function1377        #[weight = T::WeightInfo::set_chain_limits()]1378        pub fn set_chain_limits(1379            origin,1380            limits: ChainLimits1381        ) -> DispatchResult {13821383            #[cfg(not(feature = "runtime-benchmarks"))]1384            ensure_root(origin)?;13851386            <ChainLimit>::put(limits);1387            Ok(())1388        }13891390        /// Enable smart contract self-sponsoring.1391        /// 1392        /// # Permissions1393        /// 1394        /// * Contract Owner1395        /// 1396        /// # Arguments1397        /// 1398        /// * contract address1399        /// * enable flag1400        /// 1401        #[weight = T::WeightInfo::enable_contract_sponsoring()]1402        pub fn enable_contract_sponsoring(1403            origin,1404            contract_address: T::AccountId,1405            enable: bool1406        ) -> DispatchResult {14071408            let sender = ensure_signed(origin)?;14091410            #[cfg(feature = "runtime-benchmarks")]1411            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14121413            Self::ensure_contract_owned(sender, &contract_address)?;14141415            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1416            Ok(())1417        }14181419        /// Set the rate limit for contract sponsoring to specified number of blocks.1420        /// 1421        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1422        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1423        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1424        /// from contract endowment if there are at least B blocks between such transactions. 1425        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1426        /// 1427        /// # Permissions1428        /// 1429        /// * Contract Owner1430        /// 1431        /// # Arguments1432        /// 1433        /// -`contract_address`: Address of the contract to sponsor1434        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1435        /// 1436        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1437        pub fn set_contract_sponsoring_rate_limit(1438            origin,1439            contract_address: T::AccountId,1440            rate_limit: T::BlockNumber1441        ) -> DispatchResult {1442            let sender = ensure_signed(origin)?;14431444            #[cfg(feature = "runtime-benchmarks")]1445            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14461447            Self::ensure_contract_owned(sender, &contract_address)?;1448            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1449            Ok(())1450        }14511452        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1453        /// 1454        /// # Permissions1455        /// 1456        /// * Address that deployed smart contract.1457        /// 1458        /// # Arguments1459        /// 1460        /// -`contract_address`: Address of the contract.1461        /// 1462        /// - `enable`: .  1463        #[weight = T::WeightInfo::toggle_contract_white_list()]1464        pub fn toggle_contract_white_list(1465            origin,1466            contract_address: T::AccountId,1467            enable: bool1468        ) -> DispatchResult {1469            let sender = ensure_signed(origin)?;14701471            #[cfg(feature = "runtime-benchmarks")]1472            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14731474            Self::ensure_contract_owned(sender, &contract_address)?;1475            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1476            Ok(())1477        }1478        1479        /// Add an address to smart contract white list.1480        /// 1481        /// # Permissions1482        /// 1483        /// * Address that deployed smart contract.1484        /// 1485        /// # Arguments1486        /// 1487        /// -`contract_address`: Address of the contract.1488        ///1489        /// -`account_address`: Address to add.1490        #[weight = T::WeightInfo::add_to_contract_white_list()]1491        pub fn add_to_contract_white_list(1492            origin,1493            contract_address: T::AccountId,1494            account_address: T::AccountId1495        ) -> DispatchResult {1496            let sender = ensure_signed(origin)?;14971498            #[cfg(feature = "runtime-benchmarks")]1499            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1500            1501            Self::ensure_contract_owned(sender, &contract_address)?;      1502            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1503            Ok(())1504        }15051506        #[weight = T::WeightInfo::add_to_contract_white_list()]1507        pub fn wasm_dummy_method(1508            origin1509        ) -> DispatchResult {1510            let sender = ensure_signed(origin)?;1511            Ok(())1512        }15131514        /// Remove an address from smart contract white list.1515        /// 1516        /// # Permissions1517        /// 1518        /// * Address that deployed smart contract.1519        /// 1520        /// # Arguments1521        /// 1522        /// -`contract_address`: Address of the contract.1523        ///1524        /// -`account_address`: Address to remove.1525        #[weight = T::WeightInfo::remove_from_contract_white_list()]1526        pub fn remove_from_contract_white_list(1527            origin,1528            contract_address: T::AccountId,1529            account_address: T::AccountId1530        ) -> DispatchResult {1531            let sender = ensure_signed(origin)?;15321533            #[cfg(feature = "runtime-benchmarks")]1534            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15351536            Self::ensure_contract_owned(sender, &contract_address)?;1537            <ContractWhiteList<T>>::remove(contract_address, account_address);1538            Ok(())1539        }15401541        #[weight = T::WeightInfo::set_collection_limits()]1542        pub fn set_collection_limits(1543            origin,1544            collection_id: u32,1545            limits: CollectionLimits,1546        ) -> DispatchResult {1547            let sender = ensure_signed(origin)?;1548            Self::check_owner_permissions(collection_id, sender.clone())?;1549            let mut target_collection = <Collection<T>>::get(collection_id);1550            let chain_limits = ChainLimit::get();1551            let climits = target_collection.limits;15521553            // collection bounds1554            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1555                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1556                Error::<T>::CollectionLimitBoundsExceeded);15571558            // token_limit   check  prev1559            ensure!(climits.token_limit > limits.token_limit && 1560                limits.token_limit <= chain_limits.account_token_ownership_limit, 1561                Error::<T>::AccountTokenLimitExceeded);15621563            target_collection.limits = limits;1564            <Collection<T>>::insert(collection_id, target_collection);15651566            Ok(())1567        } 1568    }1569}15701571impl<T: Trait> Module<T> {15721573    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15741575        // check token limit and account token limit1576        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1577        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1578        1579        Ok(())1580    }15811582    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15831584        // check token limit and account token limit1585        let total_items: u32 = ItemListIndex::get(collection_id);1586        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1587        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1588        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15891590        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1591            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1592            Self::check_white_list(collection_id, owner)?;1593            Self::check_white_list(collection_id, sender)?;1594        }15951596        Ok(())1597    }15981599    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1600        match target_collection.mode1601        {1602            CollectionMode::NFT => {1603                if let CreateItemData::NFT(data) = data {1604                    // check sizes1605                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1606                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1607                } else {1608                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1609                }1610            },1611            CollectionMode::Fungible(_) => {1612                if let CreateItemData::Fungible(_) = data {1613                } else {1614                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1615                }1616            },1617            CollectionMode::ReFungible(_) => {1618                if let CreateItemData::ReFungible(data) = data {16191620                    // check sizes1621                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1622                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1623                } else {1624                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1625                }1626            },1627            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1628        };16291630        Ok(())1631    }16321633    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1634        match data1635        {1636            CreateItemData::NFT(data) => {1637                let item = NftItemType {1638                    collection: collection_id,1639                    owner,1640                    const_data: data.const_data,1641                    variable_data: data.variable_data1642                };16431644                Self::add_nft_item(item)?;1645            },1646            CreateItemData::Fungible(_) => {1647                let item = FungibleItemType {1648                    collection: collection_id,1649                    owner,1650                    value: (10 as u128).pow(collection.decimal_points as u32)1651                };16521653                Self::add_fungible_item(item)?;1654            },1655            CreateItemData::ReFungible(data) => {1656                let mut owner_list = Vec::new();1657                let value = (10 as u128).pow(collection.decimal_points as u32);1658                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16591660                let item = ReFungibleItemType {1661                    collection: collection_id,1662                    owner: owner_list,1663                    const_data: data.const_data,1664                    variable_data: data.variable_data1665                };16661667                Self::add_refungible_item(item)?;1668            }1669        };16701671        // call event1672        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16731674        Ok(())1675    }16761677    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1678        let current_index = <ItemListIndex>::get(item.collection)1679            .checked_add(1)1680            .ok_or(Error::<T>::NumOverflow)?;1681        let itemcopy = item.clone();1682        let owner = item.owner.clone();16831684        Self::add_token_index(item.collection, current_index, owner.clone())?;16851686        <ItemListIndex>::insert(item.collection, current_index);1687        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16881689        // Add current block1690        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1691        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1692        1693        // Update balance1694        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1695            .checked_add(item.value)1696            .ok_or(Error::<T>::NumOverflow)?;1697        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16981699        Ok(())1700    }17011702    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1703        let current_index = <ItemListIndex>::get(item.collection)1704            .checked_add(1)1705            .ok_or(Error::<T>::NumOverflow)?;1706        let itemcopy = item.clone();17071708        let value = item.owner.first().unwrap().fraction;1709        let owner = item.owner.first().unwrap().owner.clone();17101711        Self::add_token_index(item.collection, current_index, owner.clone())?;17121713        <ItemListIndex>::insert(item.collection, current_index);1714        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);17151716        // Add current block1717        let block_number: T::BlockNumber = 0.into();1718        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);17191720        // Update balance1721        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1722            .checked_add(value)1723            .ok_or(Error::<T>::NumOverflow)?;1724        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);17251726        Ok(())1727    }17281729    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1730        let current_index = <ItemListIndex>::get(item.collection)1731            .checked_add(1)1732            .ok_or(Error::<T>::NumOverflow)?;17331734        let item_owner = item.owner.clone();1735        let collection_id = item.collection.clone();1736        Self::add_token_index(collection_id, current_index, item.owner.clone())?;17371738        <ItemListIndex>::insert(collection_id, current_index);1739        <NftItemList<T>>::insert(collection_id, current_index, item);17401741        // Add current block1742        let block_number: T::BlockNumber = 0.into();1743        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);17441745        // Update balance1746        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1747            .checked_add(1)1748            .ok_or(Error::<T>::NumOverflow)?;1749        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17501751        Ok(())1752    }17531754    fn burn_refungible_item(1755        collection_id: CollectionId,1756        item_id: TokenId,1757        owner: T::AccountId,1758    ) -> DispatchResult {1759        ensure!(1760            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1761            Error::<T>::TokenNotFound1762        );1763        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1764        let item = collection1765            .owner1766            .iter()1767            .filter(|&i| i.owner == owner)1768            .next()1769            .unwrap();1770        Self::remove_token_index(collection_id, item_id, owner.clone())?;17711772        // remove approve list1773        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17741775        // update balance1776        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1777            .checked_sub(item.fraction)1778            .ok_or(Error::<T>::NumOverflow)?;1779        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17801781        <ReFungibleItemList<T>>::remove(collection_id, item_id);17821783        Ok(())1784    }17851786    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1787        ensure!(1788            <NftItemList<T>>::contains_key(collection_id, item_id),1789            Error::<T>::TokenNotFound1790        );1791        let item = <NftItemList<T>>::get(collection_id, item_id);1792        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17931794        // remove approve list1795        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17961797        // update balance1798        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1799            .checked_sub(1)1800            .ok_or(Error::<T>::NumOverflow)?;1801        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1802        <NftItemList<T>>::remove(collection_id, item_id);18031804        Ok(())1805    }18061807    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1808        ensure!(1809            <FungibleItemList<T>>::contains_key(collection_id, item_id),1810            Error::<T>::TokenNotFound1811        );1812        let item = <FungibleItemList<T>>::get(collection_id, item_id);1813        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;18141815        // remove approve list1816        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));18171818        // update balance1819        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1820            .checked_sub(item.value)1821            .ok_or(Error::<T>::NumOverflow)?;1822        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);18231824        <FungibleItemList<T>>::remove(collection_id, item_id);18251826        Ok(())1827    }18281829    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1830        ensure!(1831            <Collection<T>>::contains_key(collection_id),1832            Error::<T>::CollectionNotFound1833        );1834        Ok(())1835    }18361837    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1838        Self::collection_exists(collection_id)?;18391840        let target_collection = <Collection<T>>::get(collection_id);1841        ensure!(1842            subject == target_collection.owner,1843            Error::<T>::NoPermission1844        );18451846        Ok(())1847    }18481849    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1850        let target_collection = <Collection<T>>::get(collection_id);1851        let mut result: bool = subject == target_collection.owner;1852        let exists = <AdminList<T>>::contains_key(collection_id);18531854        if !result & exists {1855            if <AdminList<T>>::get(collection_id).contains(&subject) {1856                result = true1857            }1858        }18591860        result1861    }18621863    fn check_owner_or_admin_permissions(1864        collection_id: CollectionId,1865        subject: T::AccountId,1866    ) -> DispatchResult {1867        Self::collection_exists(collection_id)?;1868        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18691870        ensure!(1871            result,1872            Error::<T>::NoPermission1873        );1874        Ok(())1875    }18761877    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1878        let target_collection = <Collection<T>>::get(collection_id);18791880        match target_collection.mode {1881            CollectionMode::NFT => {1882                <NftItemList<T>>::get(collection_id, item_id).owner == subject1883            }1884            CollectionMode::Fungible(_) => {1885                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1886            }1887            CollectionMode::ReFungible(_) => {1888                <ReFungibleItemList<T>>::get(collection_id, item_id)1889                    .owner1890                    .iter()1891                    .any(|i| i.owner == subject)1892            }1893            CollectionMode::Invalid => false,1894        }1895    }18961897    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1898        let mes = Error::<T>::AddresNotInWhiteList;1899        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19001901        Ok(())1902    }19031904    fn transfer_fungible(1905        collection_id: CollectionId,1906        item_id: TokenId,1907        value: u128,1908        owner: T::AccountId,1909        new_owner: T::AccountId,1910    ) -> DispatchResult {1911        ensure!(1912            <FungibleItemList<T>>::contains_key(collection_id, item_id),1913            Error::<T>::TokenNotFound1914        );19151916        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1917        let amount = full_item.value;19181919        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19201921        // update balance1922        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1923            .checked_sub(value)1924            .ok_or(Error::<T>::NumOverflow)?;1925        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);19261927        let mut new_owner_account_id = 0;1928        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1929        if new_owner_items.len() > 0 {1930            new_owner_account_id = new_owner_items[0];1931        }19321933        // transfer1934        if amount == value && new_owner_account_id == 0 {1935            // change owner1936            // new owner do not have account1937            let mut new_full_item = full_item.clone();1938            new_full_item.owner = new_owner.clone();1939            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19401941            // update balance1942            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1943                .checked_add(value)1944                .ok_or(Error::<T>::NumOverflow)?;1945            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19461947            // update index collection1948            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1949        } else {1950            let mut new_full_item = full_item.clone();1951            new_full_item.value -= value;19521953            // separate amount1954            if new_owner_account_id > 0 {1955                // new owner has account1956                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1957                item.value += value;19581959                // update balance1960                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1961                    .checked_add(value)1962                    .ok_or(Error::<T>::NumOverflow)?;1963                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19641965                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1966            } else {1967                // new owner do not have account1968                let item = FungibleItemType {1969                    collection: collection_id,1970                    owner: new_owner.clone(),1971                    value1972                };19731974                Self::add_fungible_item(item)?;1975            }19761977            if amount == value {1978                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19791980                // remove approve list1981                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1982                <FungibleItemList<T>>::remove(collection_id, item_id);1983            }19841985            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1986        }19871988        Ok(())1989    }19901991    fn transfer_refungible(1992        collection_id: CollectionId,1993        item_id: TokenId,1994        value: u128,1995        owner: T::AccountId,1996        new_owner: T::AccountId,1997    ) -> DispatchResult {1998        ensure!(1999            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2000            Error::<T>::TokenNotFound2001        );20022003        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2004        let item = full_item2005            .owner2006            .iter()2007            .filter(|i| i.owner == owner)2008            .next()2009            .ok_or(Error::<T>::NumOverflow)?;2010        let amount = item.fraction;20112012        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20132014        // update balance2015        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2016            .checked_sub(value)2017            .ok_or(Error::<T>::NumOverflow)?;2018        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20192020        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2021            .checked_add(value)2022            .ok_or(Error::<T>::NumOverflow)?;2023        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20242025        let old_owner = item.owner.clone();2026        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20272028        // transfer2029        if amount == value && !new_owner_has_account {2030            // change owner2031            // new owner do not have account2032            let mut new_full_item = full_item.clone();2033            new_full_item2034                .owner2035                .iter_mut()2036                .find(|i| i.owner == owner)2037                .unwrap()2038                .owner = new_owner.clone();2039            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20402041            // update index collection2042            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;2043        } else {2044            let mut new_full_item = full_item.clone();2045            new_full_item2046                .owner2047                .iter_mut()2048                .find(|i| i.owner == owner)2049                .unwrap()2050                .fraction -= value;20512052            // separate amount2053            if new_owner_has_account {2054                // new owner has account2055                new_full_item2056                    .owner2057                    .iter_mut()2058                    .find(|i| i.owner == new_owner)2059                    .unwrap()2060                    .fraction += value;2061            } else {2062                // new owner do not have account2063                new_full_item.owner.push(Ownership {2064                    owner: new_owner.clone(),2065                    fraction: value,2066                });2067                Self::add_token_index(collection_id, item_id, new_owner.clone())?;2068            }20692070            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2071        }20722073        Ok(())2074    }20752076    fn transfer_nft(2077        collection_id: CollectionId,2078        item_id: TokenId,2079        sender: T::AccountId,2080        new_owner: T::AccountId,2081    ) -> DispatchResult {2082        ensure!(2083            <NftItemList<T>>::contains_key(collection_id, item_id),2084            Error::<T>::TokenNotFound2085        );20862087        let mut item = <NftItemList<T>>::get(collection_id, item_id);20882089        ensure!(2090            sender == item.owner,2091            Error::<T>::MustBeTokenOwner2092        );20932094        // update balance2095        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2096            .checked_sub(1)2097            .ok_or(Error::<T>::NumOverflow)?;2098        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20992100        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2101            .checked_add(1)2102            .ok_or(Error::<T>::NumOverflow)?;2103        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);21042105        // change owner2106        let old_owner = item.owner.clone();2107        item.owner = new_owner.clone();2108        <NftItemList<T>>::insert(collection_id, item_id, item);21092110        // update index collection2111        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;21122113        // reset approved list2114        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2115        Ok(())2116    }2117    2118    fn item_exists(2119        collection_id: CollectionId,2120        item_id: TokenId,2121        mode: &CollectionMode2122    ) -> DispatchResult {2123        match mode {2124            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2125            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2126            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2127            _ => ()2128        };2129        2130        Ok(())2131    }21322133    fn set_re_fungible_variable_data(2134        collection_id: CollectionId,2135        item_id: TokenId,2136        data: Vec<u8>2137    ) -> DispatchResult {2138        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21392140        item.variable_data = data;21412142        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21432144        Ok(())2145    }21462147    fn set_nft_variable_data(2148        collection_id: CollectionId,2149        item_id: TokenId,2150        data: Vec<u8>2151    ) -> DispatchResult {2152        let mut item = <NftItemList<T>>::get(collection_id, item_id);2153        2154        item.variable_data = data;21552156        <NftItemList<T>>::insert(collection_id, item_id, item);2157        2158        Ok(())2159    }21602161    fn init_collection(item: &CollectionType<T::AccountId>) {2162        // check params2163        assert!(2164            item.decimal_points <= MAX_DECIMAL_POINTS,2165            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2166        );2167        assert!(2168            item.name.len() <= 64,2169            "Collection name can not be longer than 63 char"2170        );2171        assert!(2172            item.name.len() <= 256,2173            "Collection description can not be longer than 255 char"2174        );2175        assert!(2176            item.token_prefix.len() <= 16,2177            "Token prefix can not be longer than 15 char"2178        );21792180        // Generate next collection ID2181        let next_id = CreatedCollectionCount::get()2182            .checked_add(1)2183            .unwrap();21842185        CreatedCollectionCount::put(next_id);2186    }21872188    fn init_nft_token(item: &NftItemType<T::AccountId>) {2189        let current_index = <ItemListIndex>::get(item.collection)2190            .checked_add(1)2191            .unwrap();21922193        let item_owner = item.owner.clone();2194        let collection_id = item.collection.clone();2195        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21962197        <ItemListIndex>::insert(collection_id, current_index);21982199        // Update balance2200        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2201            .checked_add(1)2202            .unwrap();2203        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2204    }22052206    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2207        let current_index = <ItemListIndex>::get(item.collection)2208            .checked_add(1)2209            .unwrap();2210        let owner = item.owner.clone();22112212        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22132214        <ItemListIndex>::insert(item.collection, current_index);22152216        // Update balance2217        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2218            .checked_add(item.value)2219            .unwrap();2220        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2221    }22222223    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2224        let current_index = <ItemListIndex>::get(item.collection)2225            .checked_add(1)2226            .unwrap();22272228        let value = item.owner.first().unwrap().fraction;2229        let owner = item.owner.first().unwrap().owner.clone();22302231        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22322233        <ItemListIndex>::insert(item.collection, current_index);22342235        // Update balance2236        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2237            .checked_add(value)2238            .unwrap();2239        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2240    }22412242    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {22432244        // add to account limit2245        if <AccountItemCount<T>>::contains_key(owner.clone()) {22462247            // bound Owned tokens by a single address2248            let count = <AccountItemCount<T>>::get(owner.clone());2249            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22502251            <AccountItemCount<T>>::insert(owner.clone(), count2252                .checked_add(1)2253                .ok_or(Error::<T>::NumOverflow)?);2254        }2255        else {2256            <AccountItemCount<T>>::insert(owner.clone(), 1);2257        }22582259        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2260        if list_exists {2261            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2262            let item_contains = list.contains(&item_index.clone());22632264            if !item_contains {2265                list.push(item_index.clone());2266            }22672268            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2269        } else {2270            let mut itm = Vec::new();2271            itm.push(item_index.clone());2272            <AddressTokens<T>>::insert(collection_id, owner, itm);2273            2274        }22752276        Ok(())2277    }22782279    fn remove_token_index(2280        collection_id: CollectionId,2281        item_index: TokenId,2282        owner: T::AccountId,2283    ) -> DispatchResult {22842285        // update counter2286        <AccountItemCount<T>>::insert(owner.clone(), 2287            <AccountItemCount<T>>::get(owner.clone())2288            .checked_sub(1)2289            .ok_or(Error::<T>::NumOverflow)?);229022912292        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2293        if list_exists {2294            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2295            let item_contains = list.contains(&item_index.clone());22962297            if item_contains {2298                list.retain(|&item| item != item_index);2299                <AddressTokens<T>>::insert(collection_id, owner, list);2300            }2301        }23022303        Ok(())2304    }23052306    fn move_token_index(2307        collection_id: CollectionId,2308        item_index: TokenId,2309        old_owner: T::AccountId,2310        new_owner: T::AccountId,2311    ) -> DispatchResult {2312        Self::remove_token_index(collection_id, item_index, old_owner)?;2313        Self::add_token_index(collection_id, item_index, new_owner)?;23142315        Ok(())2316    }2317    2318    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2319        if <ContractOwner<T>>::contains_key(contract.clone()) {2320            let owner = <ContractOwner<T>>::get(contract);2321            ensure!(account == owner, Error::<T>::NoPermission);2322        } else {2323            fail!(Error::<T>::NoPermission);2324        }23252326        Ok(())2327    }2328}23292330////////////////////////////////////////////////////////////////////////////////////////////////////2331// Economic models2332// #region23332334/// Fee multiplier.2335pub type Multiplier = FixedU128;23362337type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2338    <T as system::Trait>::AccountId,2339>>::Balance;2340type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2341    <T as system::Trait>::AccountId,2342>>::NegativeImbalance;23432344/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2345/// in the queue.2346#[derive(Encode, Decode, Clone, Eq, PartialEq)]2347pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2348    #[codec(compact)] BalanceOf<T>2349);23502351impl<T: Trait + Send + Sync> sp_std::fmt::Debug2352    for ChargeTransactionPayment<T>2353{2354    #[cfg(feature = "std")]2355    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2356        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2357    }2358    #[cfg(not(feature = "std"))]2359    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2360        Ok(())2361    }2362}23632364impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2365where2366    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2367    BalanceOf<T>: Send + Sync + FixedPointOperand,2368{2369    /// utility constructor. Used only in client/factory code.2370    pub fn from(fee: BalanceOf<T>) -> Self {2371        Self(fee)2372    }23732374    pub fn traditional_fee(2375        len: usize,2376        info: &DispatchInfoOf<T::Call>,2377        tip: BalanceOf<T>,2378    ) -> BalanceOf<T>2379    where2380        T::Call: Dispatchable<Info = DispatchInfo>,2381    {2382        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2383    }23842385	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2386		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2387		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2388		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2389		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2390	}23912392    fn withdraw_fee(2393        &self,2394        who: &T::AccountId,2395        call: &T::Call,2396        info: &DispatchInfoOf<T::Call>,2397        len: usize,2398    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2399        let tip = self.0;24002401        // Set fee based on call type. Creating collection costs 1 Unique.2402        // All other transactions have traditional fees so far2403        // let fee = match call.is_sub_type() {2404        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2405        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2406        //                                                 // _ => <BalanceOf<T>>::from(100)2407        // };2408        let fee = Self::traditional_fee(len, info, tip);24092410        // Determine who is paying transaction fee based on ecnomic model2411        // Parse call to extract collection ID and access collection sponsor2412        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2413            Some(Call::create_item(collection_id, _owner, _properties)) => {24142415                // check free create limit2416                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2417                {2418                    <Collection<T>>::get(collection_id).sponsor2419                } else {2420                    T::AccountId::default()2421                }2422            }2423            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2424                2425                let _collection_limits = <Collection<T>>::get(collection_id).limits;2426                let _collection_mode = <Collection<T>>::get(collection_id).mode;24272428                // sponsor timeout2429                let sponsor_transfer = match _collection_mode {2430                    CollectionMode::NFT => {24312432                        // get correct limit2433                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2434                            _collection_limits.sponsor_transfer_timeout2435                        } else {2436                            ChainLimit::get().nft_sponsor_transfer_timeout2437                        };24382439                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2440                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2441                        let limit_time = basket + limit.into();2442                        if block_number >= limit_time {2443                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2444                            true2445                        }2446                        else {2447                            false2448                        }2449                    }2450                    CollectionMode::Fungible(_) => {24512452                        // get correct limit2453                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2454                            _collection_limits.sponsor_transfer_timeout2455                        } else {2456                            ChainLimit::get().fungible_sponsor_transfer_timeout2457                        };24582459                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2460                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2461                        if basket.iter().any(|i| i.address == _new_owner.clone())2462                        {2463                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2464                            let limit_time = item.start_block + limit.into();2465                            if block_number >= limit_time {2466                                basket.retain(|x| x.address == item.address);2467                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2468                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2469                                true2470                            }2471                            else {2472                                false2473                            }2474                        }2475                        else {2476                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2477                            true2478                        }2479                    }2480                    CollectionMode::ReFungible(_) => {24812482                        // get correct limit2483                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2484                            _collection_limits.sponsor_transfer_timeout2485                        } else {2486                            ChainLimit::get().refungible_sponsor_transfer_timeout2487                        };24882489                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2490                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2491                        let limit_time = basket + limit.into();2492                        if block_number >= limit_time {2493                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2494                            true2495                        } else {2496                            false2497                        }2498                    }2499                    _ => {2500                        false2501                    },2502                };25032504                if !sponsor_transfer {2505                    T::AccountId::default()2506                } else {2507                    <Collection<T>>::get(collection_id).sponsor2508                }2509            }25102511            _ => T::AccountId::default(),2512        };25132514        // Sponsor smart contracts2515        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25162517            // On instantiation: set the contract owner2518            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {25192520                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2521                    code_hash,2522                    &data,2523                    &who,2524                );2525                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25262527                T::AccountId::default()2528            },25292530            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2531            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25322533                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25342535                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2536                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2537                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2538                  2539                if !owned_contract && white_list_enabled {2540                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2541                        return Err(InvalidTransaction::Call.into());2542                    }2543                }25442545                let mut sponsor_transfer = false;2546                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2547                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2548                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2549                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2550                    let limit_time = last_tx_block + rate_limit;25512552                    if block_number >= limit_time {2553                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2554                        sponsor_transfer = true;2555                    }2556                } else {2557                    sponsor_transfer = false;2558                }2559               2560                2561                let mut sp = T::AccountId::default();2562                if sponsor_transfer {2563                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2564                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2565                            sp = called_contract;2566                        }2567                    }2568                }25692570                sp2571            },25722573            _ => sponsor,2574        };25752576        let mut who_pays_fee: T::AccountId = sponsor.clone();2577        if sponsor == T::AccountId::default() {2578            who_pays_fee = who.clone();2579        }25802581        // Only mess with balances if fee is not zero.2582        if fee.is_zero() {2583            return Ok((fee, None));2584        }25852586        match <T as transaction_payment::Trait>::Currency::withdraw(2587            &who_pays_fee,2588            fee,2589            if tip.is_zero() {2590                WithdrawReason::TransactionPayment.into()2591            } else {2592                WithdrawReason::TransactionPayment | WithdrawReason::Tip2593            },2594            ExistenceRequirement::KeepAlive,2595        ) {2596            Ok(imbalance) => Ok((fee, Some(imbalance))),2597            Err(_) => Err(InvalidTransaction::Payment.into()),2598        }2599    }2600}260126022603impl<T: Trait + Send + Sync> SignedExtension2604    for ChargeTransactionPayment<T>2605where2606    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2607    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2608{2609    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2610    type AccountId = T::AccountId;2611    type Call = T::Call;2612    type AdditionalSigned = ();2613    type Pre = (2614        BalanceOf<T>,2615        Self::AccountId,2616        Option<NegativeImbalanceOf<T>>,2617        BalanceOf<T>,2618    );2619    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2620        Ok(())2621    }26222623    fn validate(2624        &self,2625        who: &Self::AccountId,2626        call: &Self::Call,2627        info: &DispatchInfoOf<Self::Call>,2628        len: usize,2629    ) -> TransactionValidity {2630		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2631		Ok(ValidTransaction {2632			priority: Self::get_priority(len, info, fee),2633			..Default::default()2634		})2635    }26362637    fn pre_dispatch(2638        self,2639        who: &Self::AccountId,2640        call: &Self::Call,2641        info: &DispatchInfoOf<Self::Call>,2642        len: usize,2643    ) -> Result<Self::Pre, TransactionValidityError> {2644        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2645        Ok((self.0, who.clone(), imbalance, fee))2646    }26472648    fn post_dispatch(2649        pre: Self::Pre,2650        info: &DispatchInfoOf<Self::Call>,2651        post_info: &PostDispatchInfoOf<Self::Call>,2652        len: usize,2653        _result: &DispatchResult,2654    ) -> Result<(), TransactionValidityError> {2655        let (tip, who, imbalance, fee) = pre;2656        if let Some(payed) = imbalance {2657            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2658                len as u32, info, post_info, tip,2659            );2660            let refund = fee.saturating_sub(actual_fee);2661            let actual_payment =2662                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2663                    &who, refund,2664                ) {2665                    Ok(refund_imbalance) => {2666                        // The refund cannot be larger than the up front payed max weight.2667                        // `PostDispatchInfo::calc_unspent` guards against such a case.2668                        match payed.offset(refund_imbalance) {2669                            Ok(actual_payment) => actual_payment,2670                            Err(_) => return Err(InvalidTransaction::Payment.into()),2671                        }2672                    }2673                    // We do not recreate the account using the refund. The up front payment2674                    // is gone in that case.2675                    Err(_) => payed,2676                };2677            let imbalances = actual_payment.split(tip);2678            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2679                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2680            );2681        }2682        Ok(())2683    }2684}26852686// #endregion
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -143,7 +143,7 @@
     spec_name: create_runtime_str!("nft"),
     impl_name: create_runtime_str!("nft"),
     authoring_version: 1,
-    spec_version: 2,
+    spec_version: 3,
     impl_version: 1,
     apis: RUNTIME_API_VERSIONS,
     transaction_version: 1,