git.delta.rocks / unique-network / refs/commits / 200a8c66fee4

difftreelog

source

pallets/nft/src/lib.rs94.3 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use 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 sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    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 owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;423        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;424        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;425426        // Contract Sponsorship and Ownership427        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;428        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;429        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;430        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;431        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 432        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 433    }434    add_extra_genesis {435        build(|config: &GenesisConfig<T>| {436            // Modification of storage437            for (_num, _c) in &config.collection {438                <Module<T>>::init_collection(_c);439            }440441            for (_num, _c, _i) in &config.nft_item_id {442                <Module<T>>::init_nft_token(*_c, _i);443            }444445            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {446                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);447            }448449            for (_num, _c, _i) in &config.refungible_item_id {450                <Module<T>>::init_refungible_token(*_c, _i);451            }452        })453    }454}455456decl_event!(457    pub enum Event<T>458    where459        AccountId = <T as system::Trait>::AccountId,460    {461        /// New collection was created462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Globally unique identifier of newly created collection.466        /// 467        /// * mode: [CollectionMode] converted into u8.468        /// 469        /// * account_id: Collection owner.470        Created(CollectionId, u8, AccountId),471472        /// New item was created.473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Id of the collection where item was created.477        /// 478        /// * item_id: Id of an item. Unique within the collection.479        ItemCreated(CollectionId, TokenId),480481        /// Collection item was burned.482        /// 483        /// # Arguments484        /// 485        /// collection_id.486        /// 487        /// item_id: Identifier of burned NFT.488        ItemDestroyed(CollectionId, TokenId),489    }490);491492decl_module! {493    pub struct Module<T: Trait> for enum Call where origin: T::Origin {494495        fn deposit_event() = default;496        type Error = Error<T>;497498        fn on_initialize(now: T::BlockNumber) -> Weight {499500            if ChainVersion::get() < 2501            {502                let value = NextCollectionID::get();503                CreatedCollectionCount::put(value);504                ChainVersion::put(2);505            }506507            0508        }509510        /// 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.511        /// 512        /// # Permissions513        /// 514        /// * Anyone.515        /// 516        /// # Arguments517        /// 518        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.519        /// 520        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.521        /// 522        /// * token_prefix: UTF-8 string with token prefix.523        /// 524        /// * mode: [CollectionMode] collection type and type dependent data.525        // returns collection ID526        #[weight = T::WeightInfo::create_collection()]527        pub fn create_collection(origin,528                                 collection_name: Vec<u16>,529                                 collection_description: Vec<u16>,530                                 token_prefix: Vec<u8>,531                                 mode: CollectionMode) -> DispatchResult {532533            // Anyone can create a collection534            let who = ensure_signed(origin)?;535536            let decimal_points = match mode {537                CollectionMode::Fungible(points) => points,538                CollectionMode::ReFungible(points) => points,539                _ => 0540            };541542            // bound Total number of collections543            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);544545            // check params546            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);547548            let mut name = collection_name.to_vec();549            name.push(0);550            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);551552            let mut description = collection_description.to_vec();553            description.push(0);554            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);555556            let mut prefix = token_prefix.to_vec();557            prefix.push(0);558            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);559560            // Generate next collection ID561            let next_id = CreatedCollectionCount::get()562                .checked_add(1)563                .ok_or(Error::<T>::NumOverflow)?;564565            // bound counter566            let total = CollectionCount::get()567                .checked_add(1)568                .ok_or(Error::<T>::NumOverflow)?;569570            CreatedCollectionCount::put(next_id);571            CollectionCount::put(total);572573            // Create new collection574            let new_collection = CollectionType {575                owner: who.clone(),576                name: name,577                mode: mode.clone(),578                mint_mode: false,579                access: AccessMode::Normal,580                description: description,581                decimal_points: decimal_points,582                token_prefix: prefix,583                offchain_schema: Vec::new(),584                schema_version: SchemaVersion::ImageURL,585                sponsor: T::AccountId::default(),586                sponsor_confirmed: false,587                variable_on_chain_schema: Vec::new(),588                const_on_chain_schema: Vec::new(),589                limits: CollectionLimits::default(),590            };591592            // Add new collection to map593            <Collection<T>>::insert(next_id, new_collection);594595            // call event596            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));597598            Ok(())599        }600601        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.602        /// 603        /// # Permissions604        /// 605        /// * Collection Owner.606        /// 607        /// # Arguments608        /// 609        /// * collection_id: collection to destroy.610        #[weight = T::WeightInfo::destroy_collection()]611        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {612613            let sender = ensure_signed(origin)?;614            Self::check_owner_permissions(collection_id, sender)?;615616            <AddressTokens<T>>::remove_prefix(collection_id);617            <Allowances<T>>::remove_prefix(collection_id);618            <Balance<T>>::remove_prefix(collection_id);619            <ItemListIndex>::remove(collection_id);620            <AdminList<T>>::remove(collection_id);621            <Collection<T>>::remove(collection_id);622            <WhiteList<T>>::remove_prefix(collection_id);623624            <NftItemList<T>>::remove_prefix(collection_id);625            <FungibleItemList<T>>::remove_prefix(collection_id);626            <ReFungibleItemList<T>>::remove_prefix(collection_id);627628            <NftTransferBasket<T>>::remove_prefix(collection_id);629            <FungibleTransferBasket<T>>::remove_prefix(collection_id);630            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);631632            if CollectionCount::get() > 0633            {634                // bound couter635                let total = CollectionCount::get()636                    .checked_sub(1)637                    .ok_or(Error::<T>::NumOverflow)?;638639                CollectionCount::put(total);640            }641642            Ok(())643        }644645        /// Add an address to white list.646        /// 647        /// # Permissions648        /// 649        /// * Collection Owner650        /// * Collection Admin651        /// 652        /// # Arguments653        /// 654        /// * collection_id.655        /// 656        /// * address.657        #[weight = T::WeightInfo::add_to_white_list()]658        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{659660            let sender = ensure_signed(origin)?;661            Self::check_owner_or_admin_permissions(collection_id, sender)?;662663            <WhiteList<T>>::insert(collection_id, address, true);664            665            Ok(())666        }667668        /// Remove an address from white list.669        /// 670        /// # Permissions671        /// 672        /// * Collection Owner673        /// * Collection Admin674        /// 675        /// # Arguments676        /// 677        /// * collection_id.678        /// 679        /// * address.680        #[weight = T::WeightInfo::remove_from_white_list()]681        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{682683            let sender = ensure_signed(origin)?;684            Self::check_owner_or_admin_permissions(collection_id, sender)?;685686            <WhiteList<T>>::remove(collection_id, address);687688            Ok(())689        }690691        /// Toggle between normal and white list access for the methods with access for `Anyone`.692        /// 693        /// # Permissions694        /// 695        /// * Collection Owner.696        /// 697        /// # Arguments698        /// 699        /// * collection_id.700        /// 701        /// * mode: [AccessMode]702        #[weight = T::WeightInfo::set_public_access_mode()]703        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult704        {705            let sender = ensure_signed(origin)?;706707            Self::check_owner_permissions(collection_id, sender)?;708            let mut target_collection = <Collection<T>>::get(collection_id);709            target_collection.access = mode;710            <Collection<T>>::insert(collection_id, target_collection);711712            Ok(())713        }714715        /// Allows Anyone to create tokens if:716        /// * White List is enabled, and717        /// * Address is added to white list, and718        /// * This method was called with True parameter719        /// 720        /// # Permissions721        /// * Collection Owner722        ///723        /// # Arguments724        /// 725        /// * collection_id.726        /// 727        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.728        #[weight = T::WeightInfo::set_mint_permission()]729        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult730        {731            let sender = ensure_signed(origin)?;732733            Self::check_owner_permissions(collection_id, sender)?;734            let mut target_collection = <Collection<T>>::get(collection_id);735            target_collection.mint_mode = mint_permission;736            <Collection<T>>::insert(collection_id, target_collection);737738            Ok(())739        }740741        /// Change the owner of the collection.742        /// 743        /// # Permissions744        /// 745        /// * Collection Owner.746        /// 747        /// # Arguments748        /// 749        /// * collection_id.750        /// 751        /// * new_owner.752        #[weight = T::WeightInfo::change_collection_owner()]753        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {754755            let sender = ensure_signed(origin)?;756            Self::check_owner_permissions(collection_id, sender)?;757            let mut target_collection = <Collection<T>>::get(collection_id);758            target_collection.owner = new_owner;759            <Collection<T>>::insert(collection_id, target_collection);760761            Ok(())762        }763764        /// Adds an admin of the Collection.765        /// 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. 766        /// 767        /// # Permissions768        /// 769        /// * Collection Owner.770        /// * Collection Admin.771        /// 772        /// # Arguments773        /// 774        /// * collection_id: ID of the Collection to add admin for.775        /// 776        /// * new_admin_id: Address of new admin to add.777        #[weight = T::WeightInfo::add_collection_admin()]778        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {779780            let sender = ensure_signed(origin)?;781            Self::check_owner_or_admin_permissions(collection_id, sender)?;782            let mut admin_arr: Vec<T::AccountId> = Vec::new();783784            if <AdminList<T>>::contains_key(collection_id)785            {786                admin_arr = <AdminList<T>>::get(collection_id);787                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);788            }789790            // Number of collection admins791            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);792793            admin_arr.push(new_admin_id);794            <AdminList<T>>::insert(collection_id, admin_arr);795796            Ok(())797        }798799        /// 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.800        ///801        /// # Permissions802        /// 803        /// * Collection Owner.804        /// * Collection Admin.805        /// 806        /// # Arguments807        /// 808        /// * collection_id: ID of the Collection to remove admin for.809        /// 810        /// * account_id: Address of admin to remove.811        #[weight = T::WeightInfo::remove_collection_admin()]812        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {813814            let sender = ensure_signed(origin)?;815            Self::check_owner_or_admin_permissions(collection_id, sender)?;816817            if <AdminList<T>>::contains_key(collection_id)818            {819                let mut admin_arr = <AdminList<T>>::get(collection_id);820                admin_arr.retain(|i| *i != account_id);821                <AdminList<T>>::insert(collection_id, admin_arr);822            }823824            Ok(())825        }826827        /// # Permissions828        /// 829        /// * Collection Owner830        /// 831        /// # Arguments832        /// 833        /// * collection_id.834        /// 835        /// * new_sponsor.836        #[weight = T::WeightInfo::set_collection_sponsor()]837        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {838839            let sender = ensure_signed(origin)?;840            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);841842            let mut target_collection = <Collection<T>>::get(collection_id);843            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);844845            target_collection.sponsor = new_sponsor;846            target_collection.sponsor_confirmed = false;847            <Collection<T>>::insert(collection_id, target_collection);848849            Ok(())850        }851852        /// # Permissions853        /// 854        /// * Sponsor.855        /// 856        /// # Arguments857        /// 858        /// * collection_id.859        #[weight = T::WeightInfo::confirm_sponsorship()]860        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {861862            let sender = ensure_signed(origin)?;863            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);864865            let mut target_collection = <Collection<T>>::get(collection_id);866            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);867868            target_collection.sponsor_confirmed = true;869            <Collection<T>>::insert(collection_id, target_collection);870871            Ok(())872        }873874        /// Switch back to pay-per-own-transaction model.875        ///876        /// # Permissions877        ///878        /// * Collection owner.879        /// 880        /// # Arguments881        /// 882        /// * collection_id.883        #[weight = T::WeightInfo::remove_collection_sponsor()]884        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {885886            let sender = ensure_signed(origin)?;887            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);888889            let mut target_collection = <Collection<T>>::get(collection_id);890            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);891892            target_collection.sponsor = T::AccountId::default();893            target_collection.sponsor_confirmed = false;894            <Collection<T>>::insert(collection_id, target_collection);895896            Ok(())897        }898899        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.900        /// 901        /// # Permissions902        /// 903        /// * Collection Owner.904        /// * Collection Admin.905        /// * Anyone if906        ///     * White List is enabled, and907        ///     * Address is added to white list, and908        ///     * MintPermission is enabled (see SetMintPermission method)909        /// 910        /// # Arguments911        /// 912        /// * collection_id: ID of the collection.913        /// 914        /// * owner: Address, initial owner of the NFT.915        ///916        /// * data: Token data to store on chain.917        // #[weight =918        // (130_000_000 as Weight)919        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))920        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))921        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]922923        #[weight = T::WeightInfo::create_item(data.len())]924        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {925926            let sender = ensure_signed(origin)?;927928            Self::collection_exists(collection_id)?;929930            let target_collection = <Collection<T>>::get(collection_id);931932            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;933            Self::validate_create_item_args(&target_collection, &data)?;934            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;935936            Ok(())937        }938939        /// This method creates multiple instances of NFT Collection created with CreateCollection method.940        /// 941        /// # Permissions942        /// 943        /// * Collection Owner.944        /// * Collection Admin.945        /// * Anyone if946        ///     * White List is enabled, and947        ///     * Address is added to white list, and948        ///     * MintPermission is enabled (see SetMintPermission method)949        /// 950        /// # Arguments951        /// 952        /// * collection_id: ID of the collection.953        /// 954        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].955        /// 956        /// * owner: Address, initial owner of the NFT.957        #[weight = T::WeightInfo::create_item(items_data.into_iter()958                               .map(|data| { data.len() })959                               .sum())]960        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {961962            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);963            let sender = ensure_signed(origin)?;964965            Self::collection_exists(collection_id)?;966            let target_collection = <Collection<T>>::get(collection_id);967968            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;969970            for data in &items_data {971                Self::validate_create_item_args(&target_collection, data)?;972            }973            for data in &items_data {974                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;975            }976977            Ok(())978        }979980        /// Destroys a concrete instance of NFT.981        /// 982        /// # Permissions983        /// 984        /// * Collection Owner.985        /// * Collection Admin.986        /// * Current NFT Owner.987        /// 988        /// # Arguments989        /// 990        /// * collection_id: ID of the collection.991        /// 992        /// * item_id: ID of NFT to burn.993        #[weight = T::WeightInfo::burn_item()]994        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {995996            let sender = ensure_signed(origin)?;997            Self::collection_exists(collection_id)?;998999            // Transfer permissions check1000            let target_collection = <Collection<T>>::get(collection_id);1001            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1002                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1003                Error::<T>::NoPermission);10041005            if target_collection.access == AccessMode::WhiteList {1006                Self::check_white_list(collection_id, &sender)?;1007            }10081009            match target_collection.mode1010            {1011                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1012                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1013                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1014                _ => ()1015            };10161017            // call event1018            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10191020            Ok(())1021        }10221023        /// Change ownership of the token.1024        /// 1025        /// # Permissions1026        /// 1027        /// * Collection Owner1028        /// * Collection Admin1029        /// * Current NFT owner1030        ///1031        /// # Arguments1032        /// 1033        /// * recipient: Address of token recipient.1034        /// 1035        /// * collection_id.1036        /// 1037        /// * item_id: ID of the item1038        ///     * Non-Fungible Mode: Required.1039        ///     * Fungible Mode: Ignored.1040        ///     * Re-Fungible Mode: Required.1041        /// 1042        /// * value: Amount to transfer.1043        ///     * Non-Fungible Mode: Ignored1044        ///     * Fungible Mode: Must specify transferred amount1045        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1046        #[weight = T::WeightInfo::transfer()]1047        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10481049            let sender = ensure_signed(origin)?;1050            let target_collection = <Collection<T>>::get(collection_id);10511052            // Limits check1053            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10541055            // Transfer permissions check1056            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1057                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1058                Error::<T>::NoPermission);10591060            if target_collection.access == AccessMode::WhiteList {1061                Self::check_white_list(collection_id, &sender)?;1062                Self::check_white_list(collection_id, &recipient)?;1063            }10641065            match target_collection.mode1066            {1067                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1068                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1069                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1070                _ => ()1071            };10721073            Ok(())1074        }10751076        /// Set, change, or remove approved address to transfer the ownership of the NFT.1077        /// 1078        /// # Permissions1079        /// 1080        /// * Collection Owner1081        /// * Collection Admin1082        /// * Current NFT owner1083        /// 1084        /// # Arguments1085        /// 1086        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1087        /// 1088        /// * collection_id.1089        /// 1090        /// * item_id: ID of the item.1091        #[weight = T::WeightInfo::approve()]1092        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10931094            let sender = ensure_signed(origin)?;10951096            // Transfer permissions check1097            let target_collection = <Collection<T>>::get(collection_id);1098            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1099                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1100                Error::<T>::NoPermission);11011102            if target_collection.access == AccessMode::WhiteList {1103                Self::check_white_list(collection_id, &sender)?;1104                Self::check_white_list(collection_id, &spender)?;1105            }11061107            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1108            let mut allowance: u128 = amount;1109            if allowance_exists {1110                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1111            }1112            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11131114            Ok(())1115        }1116        1117        /// 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.1118        /// 1119        /// # Permissions1120        /// * Collection Owner1121        /// * Collection Admin1122        /// * Current NFT owner1123        /// * Address approved by current NFT owner1124        /// 1125        /// # Arguments1126        /// 1127        /// * from: Address that owns token.1128        /// 1129        /// * recipient: Address of token recipient.1130        /// 1131        /// * collection_id.1132        /// 1133        /// * item_id: ID of the item.1134        /// 1135        /// * value: Amount to transfer.1136        #[weight = T::WeightInfo::transfer_from()]1137        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11381139            let sender = ensure_signed(origin)?;1140            let mut appoved_transfer = false;11411142            // Check approval1143            let mut approval: u128 = 0;1144            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1145                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1146                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1147                appoved_transfer = true;1148            }11491150            let target_collection = <Collection<T>>::get(collection_id);11511152            // Limits check1153            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11541155            // Transfer permissions check         1156            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1157                Error::<T>::NoPermission);11581159            if target_collection.access == AccessMode::WhiteList {1160                Self::check_white_list(collection_id, &sender)?;1161                Self::check_white_list(collection_id, &recipient)?;1162            }11631164            // Reduce approval by transferred amount or remove if remaining approval drops to 01165            if approval - value > 0 {1166                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1167            }1168            else {1169                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1170            }11711172            match target_collection.mode1173            {1174                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1175                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1176                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1177                _ => ()1178            };11791180            Ok(())1181        }11821183        #[weight = 0]1184        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11851186            // let no_perm_mes = "You do not have permissions to modify this collection";1187            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1188            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1189            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11901191            // // on_nft_received  call11921193            // Self::transfer(origin, collection_id, item_id, new_owner)?;11941195            Ok(())1196        }11971198        /// Set off-chain data schema.1199        /// 1200        /// # Permissions1201        /// 1202        /// * Collection Owner1203        /// * Collection Admin1204        /// 1205        /// # Arguments1206        /// 1207        /// * collection_id.1208        /// 1209        /// * schema: String representing the offchain data schema.1210        #[weight = T::WeightInfo::set_variable_meta_data()]1211        pub fn set_variable_meta_data (1212            origin,1213            collection_id: CollectionId,1214            item_id: TokenId,1215            data: Vec<u8>1216        ) -> DispatchResult {1217            let sender = ensure_signed(origin)?;1218            1219            Self::collection_exists(collection_id)?;1220            1221            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12221223            // Modify permissions check1224            let target_collection = <Collection<T>>::get(collection_id);1225            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1226                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1227                Error::<T>::NoPermission);12281229            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12301231            match target_collection.mode1232            {1233                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1234                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1235                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1236                _ => fail!(Error::<T>::UnexpectedCollectionType)1237            };12381239            Ok(())1240        }1241 1242        /// Set schema standard1243        /// ImageURL1244        /// Unique1245        /// 1246        /// # Permissions1247        /// 1248        /// * Collection Owner1249        /// * Collection Admin1250        /// 1251        /// # Arguments1252        /// 1253        /// * collection_id.1254        /// 1255        /// * schema: SchemaVersion: enum1256        #[weight = T::WeightInfo::set_schema_version()]1257        pub fn set_schema_version(1258            origin,1259            collection_id: CollectionId,1260            version: SchemaVersion1261        ) -> DispatchResult {1262            let sender = ensure_signed(origin)?;1263            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1264            let mut target_collection = <Collection<T>>::get(collection_id);1265            target_collection.schema_version = version;1266            <Collection<T>>::insert(collection_id, target_collection);12671268            Ok(())1269        }12701271        /// Set off-chain data schema.1272        /// 1273        /// # Permissions1274        /// 1275        /// * Collection Owner1276        /// * Collection Admin1277        /// 1278        /// # Arguments1279        /// 1280        /// * collection_id.1281        /// 1282        /// * schema: String representing the offchain data schema.1283        #[weight = T::WeightInfo::set_offchain_schema()]1284        pub fn set_offchain_schema(1285            origin,1286            collection_id: CollectionId,1287            schema: Vec<u8>1288        ) -> DispatchResult {1289            let sender = ensure_signed(origin)?;1290            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12911292            let mut target_collection = <Collection<T>>::get(collection_id);1293            target_collection.offchain_schema = schema;1294            <Collection<T>>::insert(collection_id, target_collection);12951296            Ok(())1297        }12981299        /// Set const on-chain data schema.1300        /// 1301        /// # Permissions1302        /// 1303        /// * Collection Owner1304        /// * Collection Admin1305        /// 1306        /// # Arguments1307        /// 1308        /// * collection_id.1309        /// 1310        /// * schema: String representing the const on-chain data schema.1311        #[weight = T::WeightInfo::set_const_on_chain_schema()]1312        pub fn set_const_on_chain_schema (1313            origin,1314            collection_id: CollectionId,1315            schema: Vec<u8>1316        ) -> DispatchResult {1317            let sender = ensure_signed(origin)?;1318            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13191320            let mut target_collection = <Collection<T>>::get(collection_id);1321            target_collection.const_on_chain_schema = schema;1322            <Collection<T>>::insert(collection_id, target_collection);13231324            Ok(())1325        }13261327        /// Set variable on-chain data schema.1328        /// 1329        /// # Permissions1330        /// 1331        /// * Collection Owner1332        /// * Collection Admin1333        /// 1334        /// # Arguments1335        /// 1336        /// * collection_id.1337        /// 1338        /// * schema: String representing the variable on-chain data schema.1339        #[weight = T::WeightInfo::set_const_on_chain_schema()]1340        pub fn set_variable_on_chain_schema (1341            origin,1342            collection_id: CollectionId,1343            schema: Vec<u8>1344        ) -> DispatchResult {1345            let sender = ensure_signed(origin)?;1346            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13471348            let mut target_collection = <Collection<T>>::get(collection_id);1349            target_collection.variable_on_chain_schema = schema;1350            <Collection<T>>::insert(collection_id, target_collection);13511352            Ok(())1353        }13541355        // Sudo permissions function1356        #[weight = T::WeightInfo::set_chain_limits()]1357        pub fn set_chain_limits(1358            origin,1359            limits: ChainLimits1360        ) -> DispatchResult {13611362            #[cfg(not(feature = "runtime-benchmarks"))]1363            ensure_root(origin)?;13641365            <ChainLimit>::put(limits);1366            Ok(())1367        }13681369        /// Enable smart contract self-sponsoring.1370        /// 1371        /// # Permissions1372        /// 1373        /// * Contract Owner1374        /// 1375        /// # Arguments1376        /// 1377        /// * contract address1378        /// * enable flag1379        /// 1380        #[weight = T::WeightInfo::enable_contract_sponsoring()]1381        pub fn enable_contract_sponsoring(1382            origin,1383            contract_address: T::AccountId,1384            enable: bool1385        ) -> DispatchResult {13861387            let sender = ensure_signed(origin)?;13881389            #[cfg(feature = "runtime-benchmarks")]1390            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13911392            Self::ensure_contract_owned(sender, &contract_address)?;13931394            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1395            Ok(())1396        }13971398        /// Set the rate limit for contract sponsoring to specified number of blocks.1399        /// 1400        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1401        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1402        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1403        /// from contract endowment if there are at least B blocks between such transactions. 1404        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1405        /// 1406        /// # Permissions1407        /// 1408        /// * Contract Owner1409        /// 1410        /// # Arguments1411        /// 1412        /// -`contract_address`: Address of the contract to sponsor1413        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1414        /// 1415        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1416        pub fn set_contract_sponsoring_rate_limit(1417            origin,1418            contract_address: T::AccountId,1419            rate_limit: T::BlockNumber1420        ) -> DispatchResult {1421            let sender = ensure_signed(origin)?;14221423            #[cfg(feature = "runtime-benchmarks")]1424            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14251426            Self::ensure_contract_owned(sender, &contract_address)?;1427            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1428            Ok(())1429        }14301431        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1432        /// 1433        /// # Permissions1434        /// 1435        /// * Address that deployed smart contract.1436        /// 1437        /// # Arguments1438        /// 1439        /// -`contract_address`: Address of the contract.1440        /// 1441        /// - `enable`: .  1442        #[weight = T::WeightInfo::toggle_contract_white_list()]1443        pub fn toggle_contract_white_list(1444            origin,1445            contract_address: T::AccountId,1446            enable: bool1447        ) -> DispatchResult {1448            let sender = ensure_signed(origin)?;14491450            #[cfg(feature = "runtime-benchmarks")]1451            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453            Self::ensure_contract_owned(sender, &contract_address)?;1454            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1455            Ok(())1456        }1457        1458        /// Add an address to smart contract white list.1459        /// 1460        /// # Permissions1461        /// 1462        /// * Address that deployed smart contract.1463        /// 1464        /// # Arguments1465        /// 1466        /// -`contract_address`: Address of the contract.1467        ///1468        /// -`account_address`: Address to add.1469        #[weight = T::WeightInfo::add_to_contract_white_list()]1470        pub fn add_to_contract_white_list(1471            origin,1472            contract_address: T::AccountId,1473            account_address: T::AccountId1474        ) -> DispatchResult {1475            let sender = ensure_signed(origin)?;14761477            #[cfg(feature = "runtime-benchmarks")]1478            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1479            1480            Self::ensure_contract_owned(sender, &contract_address)?;      1481            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1482            Ok(())1483        }14841485        /// Remove an address from smart contract white list.1486        /// 1487        /// # Permissions1488        /// 1489        /// * Address that deployed smart contract.1490        /// 1491        /// # Arguments1492        /// 1493        /// -`contract_address`: Address of the contract.1494        ///1495        /// -`account_address`: Address to remove.1496        #[weight = T::WeightInfo::remove_from_contract_white_list()]1497        pub fn remove_from_contract_white_list(1498            origin,1499            contract_address: T::AccountId,1500            account_address: T::AccountId1501        ) -> DispatchResult {1502            let sender = ensure_signed(origin)?;15031504            #[cfg(feature = "runtime-benchmarks")]1505            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15061507            Self::ensure_contract_owned(sender, &contract_address)?;1508            <ContractWhiteList<T>>::remove(contract_address, account_address);1509            Ok(())1510        }15111512        #[weight = T::WeightInfo::set_collection_limits()]1513        pub fn set_collection_limits(1514            origin,1515            collection_id: u32,1516            limits: CollectionLimits,1517        ) -> DispatchResult {1518            let sender = ensure_signed(origin)?;1519            Self::check_owner_permissions(collection_id, sender.clone())?;1520            let mut target_collection = <Collection<T>>::get(collection_id);1521            let chain_limits = ChainLimit::get();1522            let climits = target_collection.limits;15231524            // collection bounds1525            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1526                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1527                Error::<T>::CollectionLimitBoundsExceeded);15281529            // token_limit   check  prev1530            ensure!(climits.token_limit > limits.token_limit && 1531                limits.token_limit <= chain_limits.account_token_ownership_limit, 1532                Error::<T>::AccountTokenLimitExceeded);15331534            target_collection.limits = limits;1535            <Collection<T>>::insert(collection_id, target_collection);15361537            Ok(())1538        } 1539    }1540}15411542impl<T: Trait> Module<T> {15431544    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15451546        // check token limit and account token limit1547        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1548        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1549        1550        Ok(())1551    }15521553    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15541555        // check token limit and account token limit1556        let total_items: u32 = ItemListIndex::get(collection_id);1557        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1558        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1559        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15601561        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1562            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1563            Self::check_white_list(collection_id, owner)?;1564            Self::check_white_list(collection_id, sender)?;1565        }15661567        Ok(())1568    }15691570    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1571        match target_collection.mode1572        {1573            CollectionMode::NFT => {1574                if let CreateItemData::NFT(data) = data {1575                    // check sizes1576                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1577                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1578                } else {1579                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1580                }1581            },1582            CollectionMode::Fungible(_) => {1583                if let CreateItemData::Fungible(_) = data {1584                } else {1585                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1586                }1587            },1588            CollectionMode::ReFungible(_) => {1589                if let CreateItemData::ReFungible(data) = data {15901591                    // check sizes1592                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1593                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1594                } else {1595                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1596                }1597            },1598            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1599        };16001601        Ok(())1602    }16031604    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1605        match data1606        {1607            CreateItemData::NFT(data) => {1608                let item = NftItemType {1609                    owner,1610                    const_data: data.const_data,1611                    variable_data: data.variable_data1612                };16131614                Self::add_nft_item(collection_id, item)?;1615            },1616            CreateItemData::Fungible(data) => {1617                Self::add_fungible_item(collection_id, &owner, data.value)?;1618            },1619            CreateItemData::ReFungible(data) => {1620                let mut owner_list = Vec::new();1621                let value = (10 as u128).pow(collection.decimal_points as u32);1622                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16231624                let item = ReFungibleItemType {1625                    owner: owner_list,1626                    const_data: data.const_data,1627                    variable_data: data.variable_data1628                };16291630                Self::add_refungible_item(collection_id, item)?;1631            }1632        };16331634        // call event1635        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16361637        Ok(())1638    }16391640    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16411642        // Does new owner already have an account?1643        let mut balance: u128 = 0;1644        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1645            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1646        } 16471648        // Mint 1649        let item = FungibleItemType {1650            value: balance + value1651        };1652        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16531654        // Update balance1655        let new_balance = <Balance<T>>::get(collection_id, owner)1656            .checked_add(value)1657            .ok_or(Error::<T>::NumOverflow)?;1658        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16591660        Ok(())1661    }16621663    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1664        let current_index = <ItemListIndex>::get(collection_id)1665            .checked_add(1)1666            .ok_or(Error::<T>::NumOverflow)?;1667        let itemcopy = item.clone();16681669        let value = item.owner.first().unwrap().fraction;1670        let owner = item.owner.first().unwrap().owner.clone();16711672        Self::add_token_index(collection_id, current_index, owner.clone())?;16731674        <ItemListIndex>::insert(collection_id, current_index);1675        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16761677        // Update balance1678        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1679            .checked_add(value)1680            .ok_or(Error::<T>::NumOverflow)?;1681        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16821683        Ok(())1684    }16851686    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1687        let current_index = <ItemListIndex>::get(collection_id)1688            .checked_add(1)1689            .ok_or(Error::<T>::NumOverflow)?;16901691        let item_owner = item.owner.clone();1692        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16931694        <ItemListIndex>::insert(collection_id, current_index);1695        <NftItemList<T>>::insert(collection_id, current_index, item);16961697        // Update balance1698        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1699            .checked_add(1)1700            .ok_or(Error::<T>::NumOverflow)?;1701        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17021703        Ok(())1704    }17051706    fn burn_refungible_item(1707        collection_id: CollectionId,1708        item_id: TokenId,1709        owner: T::AccountId,1710    ) -> DispatchResult {1711        ensure!(1712            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1713            Error::<T>::TokenNotFound1714        );1715        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1716        let item = collection1717            .owner1718            .iter()1719            .filter(|&i| i.owner == owner)1720            .next()1721            .unwrap();1722        Self::remove_token_index(collection_id, item_id, owner.clone())?;17231724        // update balance1725        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1726            .checked_sub(item.fraction)1727            .ok_or(Error::<T>::NumOverflow)?;1728        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17291730        <ReFungibleItemList<T>>::remove(collection_id, item_id);17311732        Ok(())1733    }17341735    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1736        ensure!(1737            <NftItemList<T>>::contains_key(collection_id, item_id),1738            Error::<T>::TokenNotFound1739        );1740        let item = <NftItemList<T>>::get(collection_id, item_id);1741        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17421743        // update balance1744        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1745            .checked_sub(1)1746            .ok_or(Error::<T>::NumOverflow)?;1747        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1748        <NftItemList<T>>::remove(collection_id, item_id);17491750        Ok(())1751    }17521753    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1754        ensure!(1755            <FungibleItemList<T>>::contains_key(collection_id, owner),1756            Error::<T>::TokenNotFound1757        );1758        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1759        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17601761        // update balance1762        let new_balance = <Balance<T>>::get(collection_id, owner)1763            .checked_sub(value)1764            .ok_or(Error::<T>::NumOverflow)?;1765        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17661767        if balance.value - value > 0 {1768            balance.value -= value;1769            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1770        }1771        else {1772            <FungibleItemList<T>>::remove(collection_id, owner);1773        }17741775        Ok(())1776    }17771778    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1779        ensure!(1780            <Collection<T>>::contains_key(collection_id),1781            Error::<T>::CollectionNotFound1782        );1783        Ok(())1784    }17851786    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1787        Self::collection_exists(collection_id)?;17881789        let target_collection = <Collection<T>>::get(collection_id);1790        ensure!(1791            subject == target_collection.owner,1792            Error::<T>::NoPermission1793        );17941795        Ok(())1796    }17971798    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1799        let target_collection = <Collection<T>>::get(collection_id);1800        let mut result: bool = subject == target_collection.owner;1801        let exists = <AdminList<T>>::contains_key(collection_id);18021803        if !result & exists {1804            if <AdminList<T>>::get(collection_id).contains(&subject) {1805                result = true1806            }1807        }18081809        result1810    }18111812    fn check_owner_or_admin_permissions(1813        collection_id: CollectionId,1814        subject: T::AccountId,1815    ) -> DispatchResult {1816        Self::collection_exists(collection_id)?;1817        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18181819        ensure!(1820            result,1821            Error::<T>::NoPermission1822        );1823        Ok(())1824    }18251826    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1827        let target_collection = <Collection<T>>::get(collection_id);18281829        match target_collection.mode {1830            CollectionMode::NFT => {1831                <NftItemList<T>>::get(collection_id, item_id).owner == subject1832            }1833            CollectionMode::Fungible(_) => {1834                <FungibleItemList<T>>::contains_key(collection_id, &subject)1835            }1836            CollectionMode::ReFungible(_) => {1837                <ReFungibleItemList<T>>::get(collection_id, item_id)1838                    .owner1839                    .iter()1840                    .any(|i| i.owner == subject)1841            }1842            CollectionMode::Invalid => false,1843        }1844    }18451846    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1847        let mes = Error::<T>::AddresNotInWhiteList;1848        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18491850        Ok(())1851    }18521853    fn transfer_fungible(1854        collection_id: CollectionId,1855        value: u128,1856        owner: &T::AccountId,1857        recipient: &T::AccountId,1858    ) -> DispatchResult {1859        ensure!(1860            <FungibleItemList<T>>::contains_key(collection_id, owner),1861            Error::<T>::TokenNotFound1862        );18631864        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1865        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18661867        // Send balance to recipient (updates balanceOf of recipient)1868        Self::add_fungible_item(collection_id, recipient, value)?;18691870        // update balanceOf of sender1871        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18721873        // Reduce or remove sender1874        if balance.value == value {1875            <FungibleItemList<T>>::remove(collection_id, owner);1876        }1877        else {1878            balance.value -= value;1879            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1880        }18811882        Ok(())1883    }18841885    fn transfer_refungible(1886        collection_id: CollectionId,1887        item_id: TokenId,1888        value: u128,1889        owner: T::AccountId,1890        new_owner: T::AccountId,1891    ) -> DispatchResult {1892        ensure!(1893            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1894            Error::<T>::TokenNotFound1895        );18961897        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1898        let item = full_item1899            .owner1900            .iter()1901            .filter(|i| i.owner == owner)1902            .next()1903            .ok_or(Error::<T>::NumOverflow)?;1904        let amount = item.fraction;19051906        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19071908        // update balance1909        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1910            .checked_sub(value)1911            .ok_or(Error::<T>::NumOverflow)?;1912        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19131914        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1915            .checked_add(value)1916            .ok_or(Error::<T>::NumOverflow)?;1917        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19181919        let old_owner = item.owner.clone();1920        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19211922        // transfer1923        if amount == value && !new_owner_has_account {1924            // change owner1925            // new owner do not have account1926            let mut new_full_item = full_item.clone();1927            new_full_item1928                .owner1929                .iter_mut()1930                .find(|i| i.owner == owner)1931                .unwrap()1932                .owner = new_owner.clone();1933            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19341935            // update index collection1936            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1937        } else {1938            let mut new_full_item = full_item.clone();1939            new_full_item1940                .owner1941                .iter_mut()1942                .find(|i| i.owner == owner)1943                .unwrap()1944                .fraction -= value;19451946            // separate amount1947            if new_owner_has_account {1948                // new owner has account1949                new_full_item1950                    .owner1951                    .iter_mut()1952                    .find(|i| i.owner == new_owner)1953                    .unwrap()1954                    .fraction += value;1955            } else {1956                // new owner do not have account1957                new_full_item.owner.push(Ownership {1958                    owner: new_owner.clone(),1959                    fraction: value,1960                });1961                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1962            }19631964            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1965        }19661967        Ok(())1968    }19691970    fn transfer_nft(1971        collection_id: CollectionId,1972        item_id: TokenId,1973        sender: T::AccountId,1974        new_owner: T::AccountId,1975    ) -> DispatchResult {1976        ensure!(1977            <NftItemList<T>>::contains_key(collection_id, item_id),1978            Error::<T>::TokenNotFound1979        );19801981        let mut item = <NftItemList<T>>::get(collection_id, item_id);19821983        ensure!(1984            sender == item.owner,1985            Error::<T>::MustBeTokenOwner1986        );19871988        // update balance1989        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1990            .checked_sub(1)1991            .ok_or(Error::<T>::NumOverflow)?;1992        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19931994        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1995            .checked_add(1)1996            .ok_or(Error::<T>::NumOverflow)?;1997        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19981999        // change owner2000        let old_owner = item.owner.clone();2001        item.owner = new_owner.clone();2002        <NftItemList<T>>::insert(collection_id, item_id, item);20032004        // update index collection2005        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20062007        Ok(())2008    }2009    2010    fn item_exists(2011        collection_id: CollectionId,2012        item_id: TokenId,2013        mode: &CollectionMode2014    ) -> DispatchResult {2015        match mode {2016            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2017            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2018            _ => ()2019        };2020        2021        Ok(())2022    }20232024    fn set_re_fungible_variable_data(2025        collection_id: CollectionId,2026        item_id: TokenId,2027        data: Vec<u8>2028    ) -> DispatchResult {2029        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20302031        item.variable_data = data;20322033        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20342035        Ok(())2036    }20372038    fn set_nft_variable_data(2039        collection_id: CollectionId,2040        item_id: TokenId,2041        data: Vec<u8>2042    ) -> DispatchResult {2043        let mut item = <NftItemList<T>>::get(collection_id, item_id);2044        2045        item.variable_data = data;20462047        <NftItemList<T>>::insert(collection_id, item_id, item);2048        2049        Ok(())2050    }20512052    fn init_collection(item: &CollectionType<T::AccountId>) {2053        // check params2054        assert!(2055            item.decimal_points <= MAX_DECIMAL_POINTS,2056            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2057        );2058        assert!(2059            item.name.len() <= 64,2060            "Collection name can not be longer than 63 char"2061        );2062        assert!(2063            item.name.len() <= 256,2064            "Collection description can not be longer than 255 char"2065        );2066        assert!(2067            item.token_prefix.len() <= 16,2068            "Token prefix can not be longer than 15 char"2069        );20702071        // Generate next collection ID2072        let next_id = CreatedCollectionCount::get()2073            .checked_add(1)2074            .unwrap();20752076        CreatedCollectionCount::put(next_id);2077    }20782079    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2080        let current_index = <ItemListIndex>::get(collection_id)2081            .checked_add(1)2082            .unwrap();20832084        let item_owner = item.owner.clone();2085        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20862087        <ItemListIndex>::insert(collection_id, current_index);20882089        // Update balance2090        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2091            .checked_add(1)2092            .unwrap();2093        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2094    }20952096    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2097        let current_index = <ItemListIndex>::get(collection_id)2098            .checked_add(1)2099            .unwrap();21002101        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();21022103        <ItemListIndex>::insert(collection_id, current_index);21042105        // Update balance2106        let new_balance = <Balance<T>>::get(collection_id, owner)2107            .checked_add(item.value)2108            .unwrap();2109        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2110    }21112112    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2113        let current_index = <ItemListIndex>::get(collection_id)2114            .checked_add(1)2115            .unwrap();21162117        let value = item.owner.first().unwrap().fraction;2118        let owner = item.owner.first().unwrap().owner.clone();21192120        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21212122        <ItemListIndex>::insert(collection_id, current_index);21232124        // Update balance2125        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2126            .checked_add(value)2127            .unwrap();2128        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2129    }21302131    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21322133        // add to account limit2134        if <AccountItemCount<T>>::contains_key(owner.clone()) {21352136            // bound Owned tokens by a single address2137            let count = <AccountItemCount<T>>::get(owner.clone());2138            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21392140            <AccountItemCount<T>>::insert(owner.clone(), count2141                .checked_add(1)2142                .ok_or(Error::<T>::NumOverflow)?);2143        }2144        else {2145            <AccountItemCount<T>>::insert(owner.clone(), 1);2146        }21472148        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2149        if list_exists {2150            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2151            let item_contains = list.contains(&item_index.clone());21522153            if !item_contains {2154                list.push(item_index.clone());2155            }21562157            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2158        } else {2159            let mut itm = Vec::new();2160            itm.push(item_index.clone());2161            <AddressTokens<T>>::insert(collection_id, owner, itm);2162            2163        }21642165        Ok(())2166    }21672168    fn remove_token_index(2169        collection_id: CollectionId,2170        item_index: TokenId,2171        owner: T::AccountId,2172    ) -> DispatchResult {21732174        // update counter2175        <AccountItemCount<T>>::insert(owner.clone(), 2176            <AccountItemCount<T>>::get(owner.clone())2177            .checked_sub(1)2178            .ok_or(Error::<T>::NumOverflow)?);217921802181        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2182        if list_exists {2183            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2184            let item_contains = list.contains(&item_index.clone());21852186            if item_contains {2187                list.retain(|&item| item != item_index);2188                <AddressTokens<T>>::insert(collection_id, owner, list);2189            }2190        }21912192        Ok(())2193    }21942195    fn move_token_index(2196        collection_id: CollectionId,2197        item_index: TokenId,2198        old_owner: T::AccountId,2199        new_owner: T::AccountId,2200    ) -> DispatchResult {2201        Self::remove_token_index(collection_id, item_index, old_owner)?;2202        Self::add_token_index(collection_id, item_index, new_owner)?;22032204        Ok(())2205    }2206    2207    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2208        if <ContractOwner<T>>::contains_key(contract.clone()) {2209            let owner = <ContractOwner<T>>::get(contract);2210            ensure!(account == owner, Error::<T>::NoPermission);2211        } else {2212            fail!(Error::<T>::NoPermission);2213        }22142215        Ok(())2216    }2217}22182219////////////////////////////////////////////////////////////////////////////////////////////////////2220// Economic models2221// #region22222223/// Fee multiplier.2224pub type Multiplier = FixedU128;22252226type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2227    <T as system::Trait>::AccountId,2228>>::Balance;2229type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2230    <T as system::Trait>::AccountId,2231>>::NegativeImbalance;22322233/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2234/// in the queue.2235#[derive(Encode, Decode, Clone, Eq, PartialEq)]2236pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2237    #[codec(compact)] BalanceOf<T>2238);22392240impl<T: Trait + Send + Sync> sp_std::fmt::Debug2241    for ChargeTransactionPayment<T>2242{2243    #[cfg(feature = "std")]2244    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2245        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2246    }2247    #[cfg(not(feature = "std"))]2248    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2249        Ok(())2250    }2251}22522253impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2254where2255    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2256    BalanceOf<T>: Send + Sync + FixedPointOperand,2257{2258    /// utility constructor. Used only in client/factory code.2259    pub fn from(fee: BalanceOf<T>) -> Self {2260        Self(fee)2261    }22622263    pub fn traditional_fee(2264        len: usize,2265        info: &DispatchInfoOf<T::Call>,2266        tip: BalanceOf<T>,2267    ) -> BalanceOf<T>2268    where2269        T::Call: Dispatchable<Info = DispatchInfo>,2270    {2271        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2272    }22732274	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2275		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2276		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2277		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2278		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2279	}22802281    fn withdraw_fee(2282        &self,2283        who: &T::AccountId,2284        call: &T::Call,2285        info: &DispatchInfoOf<T::Call>,2286        len: usize,2287    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2288        let tip = self.0;22892290        // Set fee based on call type. Creating collection costs 1 Unique.2291        // All other transactions have traditional fees so far2292        // let fee = match call.is_sub_type() {2293        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2294        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2295        //                                                 // _ => <BalanceOf<T>>::from(100)2296        // };2297        let fee = Self::traditional_fee(len, info, tip);22982299        // Only mess with balances if fee is not zero.2300        if fee.is_zero() {2301            return Ok((fee, None));2302        }23032304        // Determine who is paying transaction fee based on ecnomic model2305        // Parse call to extract collection ID and access collection sponsor2306        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2307            Some(Call::create_item(collection_id, _owner, _properties)) => {23082309                // check free create limit2310                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2311                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2312                {2313                    <Collection<T>>::get(collection_id).sponsor2314                } else {2315                    T::AccountId::default()2316                }2317            }2318            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2319                2320                let mut sponsor_transfer = false;2321                if <Collection<T>>::get(collection_id).sponsor_confirmed {23222323                    let collection_limits = <Collection<T>>::get(collection_id).limits;2324                    let collection_mode = <Collection<T>>::get(collection_id).mode;2325    2326                    // sponsor timeout2327                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2328                    sponsor_transfer = match collection_mode {2329                        CollectionMode::NFT => {2330    2331                            // get correct limit2332                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2333                                collection_limits.sponsor_transfer_timeout2334                            } else {2335                                ChainLimit::get().nft_sponsor_transfer_timeout2336                            };2337    2338                            let mut sponsored = true;2339                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2340                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2341                                let limit_time = last_tx_block + limit.into();2342                                if block_number <= limit_time {2343                                    sponsored = false;2344                                }2345                            }2346                            if sponsored {2347                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2348                            }23492350                            sponsored2351                        }2352                        CollectionMode::Fungible(_) => {2353    2354                            // get correct limit2355                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2356                                collection_limits.sponsor_transfer_timeout2357                            } else {2358                                ChainLimit::get().fungible_sponsor_transfer_timeout2359                            };2360    2361                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2362                            let mut sponsored = true;2363                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2364                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2365                                let limit_time = last_tx_block + limit.into();2366                                if block_number <= limit_time {2367                                    sponsored = false;2368                                }2369                            }2370                            if sponsored {2371                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2372                            }23732374                            sponsored2375                        }2376                        CollectionMode::ReFungible(_) => {2377    2378                            // get correct limit2379                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2380                                collection_limits.sponsor_transfer_timeout2381                            } else {2382                                ChainLimit::get().refungible_sponsor_transfer_timeout2383                            };2384    2385                            let mut sponsored = true;2386                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2387                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2388                                let limit_time = last_tx_block + limit.into();2389                                if block_number <= limit_time {2390                                    sponsored = false;2391                                }2392                            }2393                            if sponsored {2394                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2395                            }23962397                            sponsored2398                        }2399                        _ => {2400                            false2401                        },2402                    };2403                }24042405                if !sponsor_transfer {2406                    T::AccountId::default()2407                } else {2408                    <Collection<T>>::get(collection_id).sponsor2409                }2410            }24112412            _ => T::AccountId::default(),2413        };24142415        // Sponsor smart contracts2416        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24172418            // On instantiation: set the contract owner2419            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24202421                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2422                    code_hash,2423                    &data,2424                    &who,2425                );2426                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24272428                T::AccountId::default()2429            },24302431            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2432            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24332434                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24352436                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2437                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2438                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2439                  2440                if !owned_contract && white_list_enabled {2441                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2442                        return Err(InvalidTransaction::Call.into());2443                    }2444                }24452446                let mut sponsor_transfer = false;2447                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2448                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2449                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2450                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2451                    let limit_time = last_tx_block + rate_limit;24522453                    if block_number >= limit_time {2454                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2455                        sponsor_transfer = true;2456                    }2457                } else {2458                    sponsor_transfer = false;2459                }2460               2461                2462                let mut sp = T::AccountId::default();2463                if sponsor_transfer {2464                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2465                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2466                            sp = called_contract;2467                        }2468                    }2469                }24702471                sp2472            },24732474            _ => sponsor,2475        };24762477        let mut who_pays_fee: T::AccountId = sponsor.clone();2478        if sponsor == T::AccountId::default() {2479            who_pays_fee = who.clone();2480        }24812482        match <T as transaction_payment::Trait>::Currency::withdraw(2483            &who_pays_fee,2484            fee,2485            if tip.is_zero() {2486                WithdrawReason::TransactionPayment.into()2487            } else {2488                WithdrawReason::TransactionPayment | WithdrawReason::Tip2489            },2490            ExistenceRequirement::KeepAlive,2491        ) {2492            Ok(imbalance) => Ok((fee, Some(imbalance))),2493            Err(_) => Err(InvalidTransaction::Payment.into()),2494        }2495    }2496}249724982499impl<T: Trait + Send + Sync> SignedExtension2500    for ChargeTransactionPayment<T>2501where2502    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2503    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2504{2505    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2506    type AccountId = T::AccountId;2507    type Call = T::Call;2508    type AdditionalSigned = ();2509    type Pre = (2510        BalanceOf<T>,2511        Self::AccountId,2512        Option<NegativeImbalanceOf<T>>,2513        BalanceOf<T>,2514    );2515    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2516        Ok(())2517    }25182519    fn validate(2520        &self,2521        who: &Self::AccountId,2522        call: &Self::Call,2523        info: &DispatchInfoOf<Self::Call>,2524        len: usize,2525    ) -> TransactionValidity {2526		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2527		Ok(ValidTransaction {2528			priority: Self::get_priority(len, info, fee),2529			..Default::default()2530		})2531    }25322533    fn pre_dispatch(2534        self,2535        who: &Self::AccountId,2536        call: &Self::Call,2537        info: &DispatchInfoOf<Self::Call>,2538        len: usize,2539    ) -> Result<Self::Pre, TransactionValidityError> {2540        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2541        Ok((self.0, who.clone(), imbalance, fee))2542    }25432544    fn post_dispatch(2545        pre: Self::Pre,2546        info: &DispatchInfoOf<Self::Call>,2547        post_info: &PostDispatchInfoOf<Self::Call>,2548        len: usize,2549        _result: &DispatchResult,2550    ) -> Result<(), TransactionValidityError> {2551        let (tip, who, imbalance, fee) = pre;2552        if let Some(payed) = imbalance {2553            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2554                len as u32, info, post_info, tip,2555            );2556            let refund = fee.saturating_sub(actual_fee);2557            let actual_payment =2558                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2559                    &who, refund,2560                ) {2561                    Ok(refund_imbalance) => {2562                        // The refund cannot be larger than the up front payed max weight.2563                        // `PostDispatchInfo::calc_unspent` guards against such a case.2564                        match payed.offset(refund_imbalance) {2565                            Ok(actual_payment) => actual_payment,2566                            Err(_) => return Err(InvalidTransaction::Payment.into()),2567                        }2568                    }2569                    // We do not recreate the account using the refund. The up front payment2570                    // is gone in that case.2571                    Err(_) => payed,2572                };2573            let imbalances = actual_payment.split(tip);2574            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2575                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2576            );2577        }2578        Ok(())2579    }2580}25812582// #endregion