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

difftreelog

NFTPAR-183: Storage Refactoring. twox_64_concat for AccountId storage keys.

sotmorskiy2020-12-11parent: #b7831a8.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4950// Structs51// #region5253pub type CollectionId = u32;54pub type TokenId = u32;5556pub type DecimalPoints = u8;5758#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]59#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]60pub enum CollectionMode {61    Invalid,62    NFT,63    // decimal points64    Fungible(DecimalPoints),65    // decimal points66    ReFungible(DecimalPoints),67}6869impl Into<u8> for CollectionMode {70    fn into(self) -> u8 {71        match self {72            CollectionMode::Invalid => 0,73            CollectionMode::NFT => 1,74            CollectionMode::Fungible(_) => 2,75            CollectionMode::ReFungible(_) => 3,76        }77    }78}7980#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]81#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]82pub enum AccessMode {83    Normal,84    WhiteList,85}86impl Default for AccessMode {87    fn default() -> Self {88        Self::Normal89    }90}9192impl Default for CollectionMode {93    fn default() -> Self {94        Self::Invalid95    }96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct Ownership<AccountId> {101    pub owner: AccountId,102    pub fraction: u128,103}104105#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]106#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]107pub struct CollectionType<AccountId> {108    pub owner: AccountId,109    pub mode: CollectionMode,110    pub access: AccessMode,111    pub decimal_points: DecimalPoints,112    pub name: Vec<u16>,        // 64 include null escape char113    pub description: Vec<u16>, // 256 include null escape char114    pub token_prefix: Vec<u8>, // 16 include null escape char115    pub mint_mode: bool,116    pub offchain_schema: Vec<u8>,117    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender118    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship119    pub variable_on_chain_schema: Vec<u8>, //120    pub const_on_chain_schema: Vec<u8>, //121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct NftItemType<AccountId> {126    pub collection: CollectionId,127    pub owner: AccountId,128    pub const_data: Vec<u8>,129    pub variable_data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135    pub collection: CollectionId,136    pub owner: AccountId,137    pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143    pub collection: CollectionId,144    pub owner: Vec<Ownership<AccountId>>,145    pub const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct ApprovePermissions<AccountId> {152    pub approved: AccountId,153    pub amount: u128,154}155156#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]157#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]158pub struct VestingItem<AccountId, Moment> {159    pub sender: AccountId,160    pub recipient: AccountId,161    pub collection_id: CollectionId,162    pub item_id: TokenId,163    pub amount: u64,164    pub vesting_date: Moment,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct BasketItem<AccountId, BlockNumber> {170    pub address: AccountId,171    pub start_block: BlockNumber,172}173174#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]175#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]176pub struct ChainLimits {177    pub collection_numbers_limit: u32,178    pub account_token_ownership_limit: u32,179    pub collections_admins_limit: u64,180    pub custom_data_limit: u32,181182    // Timeouts for item types in passed blocks183    pub nft_sponsor_transfer_timeout: u32,184    pub fungible_sponsor_transfer_timeout: u32,185    pub refungible_sponsor_transfer_timeout: u32,186}187188pub trait WeightInfo {189	fn create_collection() -> Weight;190	fn destroy_collection() -> Weight;191	fn add_to_white_list() -> Weight;192	fn remove_from_white_list() -> Weight;193    fn set_public_access_mode() -> Weight;194    fn set_mint_permission() -> Weight;195    fn change_collection_owner() -> Weight;196    fn add_collection_admin() -> Weight;197    fn remove_collection_admin() -> Weight;198    fn set_collection_sponsor() -> Weight;199    fn confirm_sponsorship() -> Weight;200    fn remove_collection_sponsor() -> Weight;201    fn create_item(s: usize) -> Weight;202    fn burn_item() -> Weight;203    fn transfer() -> Weight;204    fn approve() -> Weight;205    fn transfer_from() -> Weight;206    fn set_offchain_schema() -> Weight;207    fn set_const_on_chain_schema() -> Weight;208    fn set_variable_on_chain_schema() -> Weight;209    fn set_variable_meta_data() -> Weight;210    fn enable_contract_sponsoring() -> Weight;211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216    pub const_data: Vec<u8>,217    pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228    pub const_data: Vec<u8>,229    pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235    NFT(CreateNftData),236    Fungible(CreateFungibleData),237    ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241    pub fn len(&self) -> usize {242        let len = match self {243            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245            _ => 0246        };247        248        return len;249    }250}251252impl From<CreateNftData> for CreateItemData {253    fn from(item: CreateNftData) -> Self {254        CreateItemData::NFT(item)255    }256}257258impl From<CreateReFungibleData> for CreateItemData {259    fn from(item: CreateReFungibleData) -> Self {260        CreateItemData::ReFungible(item)261    }262}263264impl From<CreateFungibleData> for CreateItemData {265    fn from(item: CreateFungibleData) -> Self {266        CreateItemData::Fungible(item)267    }268}269270271decl_error! {272	/// Error for non-fungible-token module.273	pub enum Error for Module<T: Trait> {274        /// Total collections bound exceeded.275        TotalCollectionsLimitExceeded,276		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.277        CollectionDecimalPointLimitExceeded, 278        /// Collection name can not be longer than 63 char.279        CollectionNameLimitExceeded, 280        /// Collection description can not be longer than 255 char.281        CollectionDescriptionLimitExceeded, 282        /// Token prefix can not be longer than 15 char.283        CollectionTokenPrefixLimitExceeded,284        /// This collection does not exist.285        CollectionNotFound,286        /// Item not exists.287        TokenNotFound,288        /// Arithmetic calculation overflow.289        NumOverflow,       290        /// Account already has admin role.291        AlreadyAdmin,  292        /// You do not own this collection.293        NoPermission,294        /// This address is not set as sponsor, use setCollectionSponsor first.295        ConfirmUnsetSponsorFail,296        /// Collection is not in mint mode.297        PublicMintingNotAllowed,298        /// Sender parameter and item owner must be equal.299        MustBeTokenOwner,300        /// Item balance not enough.301        TokenValueTooLow,302        /// Size of item is too large.303        NftSizeLimitExceeded,304        /// No approve found305        ApproveNotFound,306        /// Requested value more than approved.307        TokenValueNotEnough,308        /// Only approved addresses can call this method.309        ApproveRequired,310        /// Address is not in white list.311        AddresNotInWhiteList,312        /// Number of collection admins bound exceeded.313        CollectionAdminsLimitExceeded,314        /// Owned tokens by a single address bound exceeded.315        AddressOwnershipLimitExceeded,316        /// Length of items properties must be greater than 0.317        EmptyArgument,318        /// const_data exceeded data limit.319        TokenConstDataLimitExceeded,320        /// variable_data exceeded data limit.321        TokenVariableDataLimitExceeded,322        /// Not NFT item data used to mint in NFT collection.323        NotNftDataUsedToMintNftCollectionToken,324        /// Not Fungible item data used to mint in Fungible collection.325        NotFungibleDataUsedToMintFungibleCollectionToken,326        /// Not Re Fungible item data used to mint in Re Fungible collection.327        NotReFungibleDataUsedToMintReFungibleCollectionToken,328        /// Unexpected collection type.329        UnexpectedCollectionType,330        /// Can't store metadata in fungible tokens.331        CantStoreMetadataInFungibleTokens332	}333}334335pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {336    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;337338    /// Weight information for extrinsics in this pallet.339	type WeightInfo: WeightInfo;340}341342#[cfg(feature = "runtime-benchmarks")]343mod benchmarking;344345// #endregion346347decl_storage! {348    trait Store for Module<T: Trait> as Nft {349350        // Private members351        NextCollectionID: CollectionId;352        CreatedCollectionCount: u32;353        ChainVersion: u64;354        ItemListIndex: map hasher(identity) CollectionId => TokenId;355356        // Chain limits struct357        pub ChainLimit get(fn chain_limit) config(): ChainLimits;358359        // Bound counters360        CollectionCount: u32;361        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u32;362363        // Basic collections364        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;365        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;366        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;367368        /// Balance owner per collection map369        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(identity) T::AccountId => u128;370371        /// second parameter: item id + owner account id372        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(identity) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;373374        /// Item collections375        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;376        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;377        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;378379        /// Index list380        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(identity) T::AccountId => Vec<TokenId>;381382        /// Tokens transfer baskets383        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;384        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;385        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;386387        // Contract Sponsorship and Ownership388        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;389        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;390        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(identity) T::AccountId => T::BlockNumber;391        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(identity) T::AccountId => T::BlockNumber;392    }393    add_extra_genesis {394        build(|config: &GenesisConfig<T>| {395            // Modification of storage396            for (_num, _c) in &config.collection {397                <Module<T>>::init_collection(_c);398            }399400            for (_num, _q, _i) in &config.nft_item_id {401                <Module<T>>::init_nft_token(_i);402            }403404            for (_num, _q, _i) in &config.fungible_item_id {405                <Module<T>>::init_fungible_token(_i);406            }407408            for (_num, _q, _i) in &config.refungible_item_id {409                <Module<T>>::init_refungible_token(_i);410            }411        })412    }413}414415decl_event!(416    pub enum Event<T>417    where418        AccountId = <T as system::Trait>::AccountId,419    {420        /// New collection was created421        /// 422        /// # Arguments423        /// 424        /// * collection_id: Globally unique identifier of newly created collection.425        /// 426        /// * mode: [CollectionMode] converted into u8.427        /// 428        /// * account_id: Collection owner.429        Created(CollectionId, u8, AccountId),430431        /// New item was created.432        /// 433        /// # Arguments434        /// 435        /// * collection_id: Id of the collection where item was created.436        /// 437        /// * item_id: Id of an item. Unique within the collection.438        ItemCreated(CollectionId, TokenId),439440        /// Collection item was burned.441        /// 442        /// # Arguments443        /// 444        /// collection_id.445        /// 446        /// item_id: Identifier of burned NFT.447        ItemDestroyed(CollectionId, TokenId),448    }449);450451decl_module! {452    pub struct Module<T: Trait> for enum Call where origin: T::Origin {453454        fn deposit_event() = default;455        type Error = Error<T>;456457        fn on_initialize(now: T::BlockNumber) -> Weight {458459            if ChainVersion::get() < 2460            {461                let value = NextCollectionID::get();462                CreatedCollectionCount::put(value);463                ChainVersion::put(2);464            }465466            0467        }468469        /// 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.470        /// 471        /// # Permissions472        /// 473        /// * Anyone.474        /// 475        /// # Arguments476        /// 477        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.478        /// 479        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.480        /// 481        /// * token_prefix: UTF-8 string with token prefix.482        /// 483        /// * mode: [CollectionMode] collection type and type dependent data.484        // returns collection ID485        #[weight = T::WeightInfo::create_collection()]486        pub fn create_collection(origin,487                                 collection_name: Vec<u16>,488                                 collection_description: Vec<u16>,489                                 token_prefix: Vec<u8>,490                                 mode: CollectionMode) -> DispatchResult {491492            // Anyone can create a collection493            let who = ensure_signed(origin)?;494495            let decimal_points = match mode {496                CollectionMode::Fungible(points) => points,497                CollectionMode::ReFungible(points) => points,498                _ => 0499            };500501            // bound Total number of collections502            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);503504            // check params505            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);506507            let mut name = collection_name.to_vec();508            name.push(0);509            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);510511            let mut description = collection_description.to_vec();512            description.push(0);513            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);514515            let mut prefix = token_prefix.to_vec();516            prefix.push(0);517            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);518519            // Generate next collection ID520            let next_id = CreatedCollectionCount::get()521                .checked_add(1)522                .ok_or(Error::<T>::NumOverflow)?;523524            // bound counter525            let total = CollectionCount::get()526                .checked_add(1)527                .ok_or(Error::<T>::NumOverflow)?;528529            CreatedCollectionCount::put(next_id);530            CollectionCount::put(total);531532            // Create new collection533            let new_collection = CollectionType {534                owner: who.clone(),535                name: name,536                mode: mode.clone(),537                mint_mode: false,538                access: AccessMode::Normal,539                description: description,540                decimal_points: decimal_points,541                token_prefix: prefix,542                offchain_schema: Vec::new(),543                sponsor: T::AccountId::default(),544                unconfirmed_sponsor: T::AccountId::default(),545                variable_on_chain_schema: Vec::new(),546                const_on_chain_schema: Vec::new(),547            };548549            // Add new collection to map550            <Collection<T>>::insert(next_id, new_collection);551552            // call event553            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));554555            Ok(())556        }557558        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.559        /// 560        /// # Permissions561        /// 562        /// * Collection Owner.563        /// 564        /// # Arguments565        /// 566        /// * collection_id: collection to destroy.567        #[weight = T::WeightInfo::destroy_collection()]568        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {569570            let sender = ensure_signed(origin)?;571            Self::check_owner_permissions(collection_id, sender)?;572573            <AddressTokens<T>>::remove_prefix(collection_id);574            <ApprovedList<T>>::remove_prefix(collection_id);575            <Balance<T>>::remove_prefix(collection_id);576            <ItemListIndex>::remove(collection_id);577            <AdminList<T>>::remove(collection_id);578            <Collection<T>>::remove(collection_id);579            <WhiteList<T>>::remove(collection_id);580581            <NftItemList<T>>::remove_prefix(collection_id);582            <FungibleItemList<T>>::remove_prefix(collection_id);583            <ReFungibleItemList<T>>::remove_prefix(collection_id);584585            <NftTransferBasket<T>>::remove_prefix(collection_id);586            <FungibleTransferBasket<T>>::remove_prefix(collection_id);587            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);588589            if CollectionCount::get() > 0590            {591                // bound couter592                let total = CollectionCount::get()593                    .checked_sub(1)594                    .ok_or(Error::<T>::NumOverflow)?;595596                CollectionCount::put(total);597            }598599            Ok(())600        }601602        /// Add an address to white list.603        /// 604        /// # Permissions605        /// 606        /// * Collection Owner607        /// * Collection Admin608        /// 609        /// # Arguments610        /// 611        /// * collection_id.612        /// 613        /// * address.614        #[weight = T::WeightInfo::add_to_white_list()]615        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{616617            let sender = ensure_signed(origin)?;618            Self::check_owner_or_admin_permissions(collection_id, sender)?;619620            let mut white_list_collection: Vec<T::AccountId>;621            if <WhiteList<T>>::contains_key(collection_id) {622                white_list_collection = <WhiteList<T>>::get(collection_id);623                if !white_list_collection.contains(&address.clone())624                {625                    white_list_collection.push(address.clone());626                }627            }628            else {629                white_list_collection = Vec::new();630                white_list_collection.push(address.clone());631            }632633            <WhiteList<T>>::insert(collection_id, white_list_collection);634            Ok(())635        }636637        /// Remove an address from white list.638        /// 639        /// # Permissions640        /// 641        /// * Collection Owner642        /// * Collection Admin643        /// 644        /// # Arguments645        /// 646        /// * collection_id.647        /// 648        /// * address.649        #[weight = T::WeightInfo::remove_from_white_list()]650        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{651652            let sender = ensure_signed(origin)?;653            Self::check_owner_or_admin_permissions(collection_id, sender)?;654655            if <WhiteList<T>>::contains_key(collection_id) {656                let mut white_list_collection = <WhiteList<T>>::get(collection_id);657                if white_list_collection.contains(&address.clone())658                {659                    white_list_collection.retain(|i| *i != address.clone());660                    <WhiteList<T>>::insert(collection_id, white_list_collection);661                }662            }663664            Ok(())665        }666667        /// Toggle between normal and white list access for the methods with access for `Anyone`.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner.672        /// 673        /// # Arguments674        /// 675        /// * collection_id.676        /// 677        /// * mode: [AccessMode]678        #[weight = T::WeightInfo::set_public_access_mode()]679        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult680        {681            let sender = ensure_signed(origin)?;682683            Self::check_owner_permissions(collection_id, sender)?;684            let mut target_collection = <Collection<T>>::get(collection_id);685            target_collection.access = mode;686            <Collection<T>>::insert(collection_id, target_collection);687688            Ok(())689        }690691        /// Allows Anyone to create tokens if:692        /// * White List is enabled, and693        /// * Address is added to white list, and694        /// * This method was called with True parameter695        /// 696        /// # Permissions697        /// * Collection Owner698        ///699        /// # Arguments700        /// 701        /// * collection_id.702        /// 703        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.704        #[weight = T::WeightInfo::set_mint_permission()]705        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult706        {707            let sender = ensure_signed(origin)?;708709            Self::check_owner_permissions(collection_id, sender)?;710            let mut target_collection = <Collection<T>>::get(collection_id);711            target_collection.mint_mode = mint_permission;712            <Collection<T>>::insert(collection_id, target_collection);713714            Ok(())715        }716717        /// Change the owner of the collection.718        /// 719        /// # Permissions720        /// 721        /// * Collection Owner.722        /// 723        /// # Arguments724        /// 725        /// * collection_id.726        /// 727        /// * new_owner.728        #[weight = T::WeightInfo::change_collection_owner()]729        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {730731            let sender = ensure_signed(origin)?;732            Self::check_owner_permissions(collection_id, sender)?;733            let mut target_collection = <Collection<T>>::get(collection_id);734            target_collection.owner = new_owner;735            <Collection<T>>::insert(collection_id, target_collection);736737            Ok(())738        }739740        /// Adds an admin of the Collection.741        /// 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. 742        /// 743        /// # Permissions744        /// 745        /// * Collection Owner.746        /// * Collection Admin.747        /// 748        /// # Arguments749        /// 750        /// * collection_id: ID of the Collection to add admin for.751        /// 752        /// * new_admin_id: Address of new admin to add.753        #[weight = T::WeightInfo::add_collection_admin()]754        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {755756            let sender = ensure_signed(origin)?;757            Self::check_owner_or_admin_permissions(collection_id, sender)?;758            let mut admin_arr: Vec<T::AccountId> = Vec::new();759760            if <AdminList<T>>::contains_key(collection_id)761            {762                admin_arr = <AdminList<T>>::get(collection_id);763                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);764            }765766            // Number of collection admins767            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);768769            admin_arr.push(new_admin_id);770            <AdminList<T>>::insert(collection_id, admin_arr);771772            Ok(())773        }774775        /// 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.776        ///777        /// # Permissions778        /// 779        /// * Collection Owner.780        /// * Collection Admin.781        /// 782        /// # Arguments783        /// 784        /// * collection_id: ID of the Collection to remove admin for.785        /// 786        /// * account_id: Address of admin to remove.787        #[weight = T::WeightInfo::remove_collection_admin()]788        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {789790            let sender = ensure_signed(origin)?;791            Self::check_owner_or_admin_permissions(collection_id, sender)?;792793            if <AdminList<T>>::contains_key(collection_id)794            {795                let mut admin_arr = <AdminList<T>>::get(collection_id);796                admin_arr.retain(|i| *i != account_id);797                <AdminList<T>>::insert(collection_id, admin_arr);798            }799800            Ok(())801        }802803        /// # Permissions804        /// 805        /// * Collection Owner806        /// 807        /// # Arguments808        /// 809        /// * collection_id.810        /// 811        /// * new_sponsor.812        #[weight = T::WeightInfo::set_collection_sponsor()]813        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {814815            let sender = ensure_signed(origin)?;816            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);817818            let mut target_collection = <Collection<T>>::get(collection_id);819            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);820821            target_collection.unconfirmed_sponsor = new_sponsor;822            <Collection<T>>::insert(collection_id, target_collection);823824            Ok(())825        }826827        /// # Permissions828        /// 829        /// * Sponsor.830        /// 831        /// # Arguments832        /// 833        /// * collection_id.834        #[weight = T::WeightInfo::confirm_sponsorship()]835        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {836837            let sender = ensure_signed(origin)?;838            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);839840            let mut target_collection = <Collection<T>>::get(collection_id);841            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);842843            target_collection.sponsor = target_collection.unconfirmed_sponsor;844            target_collection.unconfirmed_sponsor = T::AccountId::default();845            <Collection<T>>::insert(collection_id, target_collection);846847            Ok(())848        }849850        /// Switch back to pay-per-own-transaction model.851        ///852        /// # Permissions853        ///854        /// * Collection owner.855        /// 856        /// # Arguments857        /// 858        /// * collection_id.859        #[weight = T::WeightInfo::remove_collection_sponsor()]860        pub fn remove_collection_sponsor(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.owner, Error::<T>::NoPermission);867868            target_collection.sponsor = T::AccountId::default();869            <Collection<T>>::insert(collection_id, target_collection);870871            Ok(())872        }873874        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.875        /// 876        /// # Permissions877        /// 878        /// * Collection Owner.879        /// * Collection Admin.880        /// * Anyone if881        ///     * White List is enabled, and882        ///     * Address is added to white list, and883        ///     * MintPermission is enabled (see SetMintPermission method)884        /// 885        /// # Arguments886        /// 887        /// * collection_id: ID of the collection.888        /// 889        /// * owner: Address, initial owner of the NFT.890        ///891        /// * data: Token data to store on chain.892        // #[weight =893        // (130_000_000 as Weight)894        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))895        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))896        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]897898        #[weight = T::WeightInfo::create_item(data.len())]899        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {900901            let sender = ensure_signed(origin)?;902903            Self::collection_exists(collection_id)?;904905            let target_collection = <Collection<T>>::get(collection_id);906907            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;908            Self::validate_create_item_args(&target_collection, &data)?;909            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;910911            Ok(())912        }913914        /// This method creates multiple instances of NFT Collection created with CreateCollection method.915        /// 916        /// # Permissions917        /// 918        /// * Collection Owner.919        /// * Collection Admin.920        /// * Anyone if921        ///     * White List is enabled, and922        ///     * Address is added to white list, and923        ///     * MintPermission is enabled (see SetMintPermission method)924        /// 925        /// # Arguments926        /// 927        /// * collection_id: ID of the collection.928        /// 929        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].930        /// 931        /// * owner: Address, initial owner of the NFT.932        #[weight = T::WeightInfo::create_item(items_data.into_iter()933                               .map(|data| { data.len() })934                               .sum())]935        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {936937            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);938            let sender = ensure_signed(origin)?;939940            Self::collection_exists(collection_id)?;941            let target_collection = <Collection<T>>::get(collection_id);942943            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;944945            for data in &items_data {946                Self::validate_create_item_args(&target_collection, data)?;947            }948            for data in &items_data {949                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;950            }951952            Ok(())953        }954955        /// Destroys a concrete instance of NFT.956        /// 957        /// # Permissions958        /// 959        /// * Collection Owner.960        /// * Collection Admin.961        /// * Current NFT Owner.962        /// 963        /// # Arguments964        /// 965        /// * collection_id: ID of the collection.966        /// 967        /// * item_id: ID of NFT to burn.968        #[weight = T::WeightInfo::burn_item()]969        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {970971            let sender = ensure_signed(origin)?;972            Self::collection_exists(collection_id)?;973974            // Transfer permissions check975            let target_collection = <Collection<T>>::get(collection_id);976            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||977                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),978                Error::<T>::NoPermission);979980            if target_collection.access == AccessMode::WhiteList {981                Self::check_white_list(collection_id, &sender)?;982            }983984            match target_collection.mode985            {986                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,987                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,988                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,989                _ => ()990            };991992            // call event993            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));994995            Ok(())996        }997998        /// Change ownership of the token.999        /// 1000        /// # Permissions1001        /// 1002        /// * Collection Owner1003        /// * Collection Admin1004        /// * Current NFT owner1005        ///1006        /// # Arguments1007        /// 1008        /// * recipient: Address of token recipient.1009        /// 1010        /// * collection_id.1011        /// 1012        /// * item_id: ID of the item1013        ///     * Non-Fungible Mode: Required.1014        ///     * Fungible Mode: Ignored.1015        ///     * Re-Fungible Mode: Required.1016        /// 1017        /// * value: Amount to transfer.1018        ///     * Non-Fungible Mode: Ignored1019        ///     * Fungible Mode: Must specify transferred amount1020        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1021        #[weight = T::WeightInfo::transfer()]1022        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10231024            let sender = ensure_signed(origin)?;10251026            // Transfer permissions check1027            let target_collection = <Collection<T>>::get(collection_id);1028            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1029                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1030                Error::<T>::NoPermission);10311032            if target_collection.access == AccessMode::WhiteList {1033                Self::check_white_list(collection_id, &sender)?;1034                Self::check_white_list(collection_id, &recipient)?;1035            }10361037            match target_collection.mode1038            {1039                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1040                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1041                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1042                _ => ()1043            };10441045            Ok(())1046        }10471048        /// Set, change, or remove approved address to transfer the ownership of the NFT.1049        /// 1050        /// # Permissions1051        /// 1052        /// * Collection Owner1053        /// * Collection Admin1054        /// * Current NFT owner1055        /// 1056        /// # Arguments1057        /// 1058        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1059        /// 1060        /// * collection_id.1061        /// 1062        /// * item_id: ID of the item.1063        #[weight = T::WeightInfo::approve()]1064        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10651066            let sender = ensure_signed(origin)?;10671068            // Transfer permissions check1069            let target_collection = <Collection<T>>::get(collection_id);1070            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1071                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1072                Error::<T>::NoPermission);10731074            if target_collection.access == AccessMode::WhiteList {1075                Self::check_white_list(collection_id, &sender)?;1076                Self::check_white_list(collection_id, &approved)?;1077            }10781079            // amount param stub1080            let amount = 100000000;10811082            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1083            if list_exists {10841085                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1086                let item_contains = list.iter().any(|i| i.approved == approved);10871088                if !item_contains {1089                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1090                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1091                }1092            } else {10931094                let mut list = Vec::new();1095                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1096                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1097            }10981099            Ok(())1100        }1101        1102        /// 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.1103        /// 1104        /// # Permissions1105        /// * Collection Owner1106        /// * Collection Admin1107        /// * Current NFT owner1108        /// * Address approved by current NFT owner1109        /// 1110        /// # Arguments1111        /// 1112        /// * from: Address that owns token.1113        /// 1114        /// * recipient: Address of token recipient.1115        /// 1116        /// * collection_id.1117        /// 1118        /// * item_id: ID of the item.1119        /// 1120        /// * value: Amount to transfer.1121        #[weight = T::WeightInfo::transfer_from()]1122        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11231124            let sender = ensure_signed(origin)?;1125            let mut appoved_transfer = false;11261127            // Check approve1128            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1129                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1130                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1131                if opt_item.is_some()1132                {1133                    appoved_transfer = true;1134                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1135                }1136            }11371138            // Transfer permissions check1139            let target_collection = <Collection<T>>::get(collection_id);1140                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1141                Error::<T>::NoPermission);11421143            if target_collection.access == AccessMode::WhiteList {1144                Self::check_white_list(collection_id, &sender)?;1145                Self::check_white_list(collection_id, &recipient)?;1146            }11471148            // remove approve1149            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1150                .into_iter().filter(|i| i.approved != sender.clone()).collect();1151            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);115211531154            match target_collection.mode1155            {1156                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1157                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1158                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1159                _ => ()1160            };11611162            Ok(())1163        }11641165        ///1166        #[weight = 0]1167        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11681169            // let no_perm_mes = "You do not have permissions to modify this collection";1170            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1171            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1172            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11731174            // // on_nft_received  call11751176            // Self::transfer(origin, collection_id, item_id, new_owner)?;11771178            Ok(())1179        }11801181        /// Set off-chain data schema.1182        /// 1183        /// # Permissions1184        /// 1185        /// * Collection Owner1186        /// * Collection Admin1187        /// 1188        /// # Arguments1189        /// 1190        /// * collection_id.1191        /// 1192        /// * schema: String representing the offchain data schema.1193        #[weight = T::WeightInfo::set_variable_meta_data()]1194        pub fn set_variable_meta_data (1195            origin,1196            collection_id: CollectionId,1197            item_id: TokenId,1198            data: Vec<u8>1199        ) -> DispatchResult {1200            let sender = ensure_signed(origin)?;1201            1202            Self::collection_exists(collection_id)?;1203            1204            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12051206            // Modify permissions check1207            let target_collection = <Collection<T>>::get(collection_id);1208            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1209                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1210                Error::<T>::NoPermission);12111212            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12131214            match target_collection.mode1215            {1216                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1217                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1218                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1219                _ => fail!(Error::<T>::UnexpectedCollectionType)1220            };12211222            Ok(())1223        }1224        12251226        /// Set off-chain data schema.1227        /// 1228        /// # Permissions1229        /// 1230        /// * Collection Owner1231        /// * Collection Admin1232        /// 1233        /// # Arguments1234        /// 1235        /// * collection_id.1236        /// 1237        /// * schema: String representing the offchain data schema.1238        #[weight = T::WeightInfo::set_offchain_schema()]1239        pub fn set_offchain_schema(1240            origin,1241            collection_id: CollectionId,1242            schema: Vec<u8>1243        ) -> DispatchResult {1244            let sender = ensure_signed(origin)?;1245            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12461247            let mut target_collection = <Collection<T>>::get(collection_id);1248            target_collection.offchain_schema = schema;1249            <Collection<T>>::insert(collection_id, target_collection);12501251            Ok(())1252        }12531254        /// Set const on-chain data schema.1255        /// 1256        /// # Permissions1257        /// 1258        /// * Collection Owner1259        /// * Collection Admin1260        /// 1261        /// # Arguments1262        /// 1263        /// * collection_id.1264        /// 1265        /// * schema: String representing the const on-chain data schema.1266        #[weight = T::WeightInfo::set_const_on_chain_schema()]1267        pub fn set_const_on_chain_schema (1268            origin,1269            collection_id: CollectionId,1270            schema: Vec<u8>1271        ) -> DispatchResult {1272            let sender = ensure_signed(origin)?;1273            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12741275            let mut target_collection = <Collection<T>>::get(collection_id);1276            target_collection.const_on_chain_schema = schema;1277            <Collection<T>>::insert(collection_id, target_collection);12781279            Ok(())1280        }12811282        /// Set variable on-chain data schema.1283        /// 1284        /// # Permissions1285        /// 1286        /// * Collection Owner1287        /// * Collection Admin1288        /// 1289        /// # Arguments1290        /// 1291        /// * collection_id.1292        /// 1293        /// * schema: String representing the variable on-chain data schema.1294        #[weight = T::WeightInfo::set_const_on_chain_schema()]1295        pub fn set_variable_on_chain_schema (1296            origin,1297            collection_id: CollectionId,1298            schema: Vec<u8>1299        ) -> DispatchResult {1300            let sender = ensure_signed(origin)?;1301            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13021303            let mut target_collection = <Collection<T>>::get(collection_id);1304            target_collection.variable_on_chain_schema = schema;1305            <Collection<T>>::insert(collection_id, target_collection);13061307            Ok(())1308        }13091310        // Sudo permissions function1311        #[weight = 0]1312        pub fn set_chain_limits(1313            origin,1314            limits: ChainLimits1315        ) -> DispatchResult {1316            ensure_root(origin)?;1317            <ChainLimit>::put(limits);1318            Ok(())1319        }13201321        /// Enable smart contract self-sponsoring.1322        /// 1323        /// # Permissions1324        /// 1325        /// * Contract Owner1326        /// 1327        /// # Arguments1328        /// 1329        /// * contract address1330        /// * enable flag1331        /// 1332        #[weight = T::WeightInfo::enable_contract_sponsoring()]1333        pub fn enable_contract_sponsoring(1334            origin,1335            contract_address: T::AccountId,1336            enable: bool1337        ) -> DispatchResult {13381339            let sender = ensure_signed(origin)?;13401341            #[cfg(feature = "runtime-benchmarks")]1342            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13431344            let mut is_owner = false;1345            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1346                let owner = <ContractOwner<T>>::get(&contract_address);1347                is_owner = sender == owner;1348            }1349            ensure!(is_owner, Error::<T>::NoPermission);13501351            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1352            Ok(())1353        }13541355        /// Set the rate limit for contract sponsoring to specified number of blocks.1356        /// 1357        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1358        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1359        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1360        /// from contract endowment if there are at least B blocks between such transactions. 1361        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1362        /// 1363        /// # Permissions1364        /// 1365        /// * Contract Owner1366        /// 1367        /// # Arguments1368        /// 1369        /// -`contract_address`: Address of the contract to sponsor1370        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1371        /// 1372        #[weight = 0]1373        pub fn set_contract_sponsoring_rate_limit(1374            origin,1375            contract_address: T::AccountId,1376            rate_limit: T::BlockNumber1377        ) -> DispatchResult {1378            let sender = ensure_signed(origin)?;1379            let mut is_owner = false;1380            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1381                let owner = <ContractOwner<T>>::get(&contract_address);1382                is_owner = sender == owner;1383            }1384            ensure!(is_owner, Error::<T>::NoPermission);13851386            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1387            Ok(())1388        }13891390        // #[cfg(feature = "runtime-benchmarks")]1391        // #[weight = 0]1392        // pub fn add_contract_sponsoring_debug(1393        //     origin,1394        //     contract_address: T::AccountId, 1395        //     owner: T::AccountId) -> DispatchResult {1396        //     let sender = ensure_signed(origin)?;1397        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);1398        //     Ok(())1399        // }1400    1401    }1402}14031404impl<T: Trait> Module<T> {14051406    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14071408        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1409            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1410            Self::check_white_list(collection_id, owner)?;1411            Self::check_white_list(collection_id, sender)?;1412        }14131414        Ok(())1415    }14161417    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1418        match target_collection.mode1419        {1420            CollectionMode::NFT => {1421                if let CreateItemData::NFT(data) = data {1422                    // check sizes1423                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1424                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1425                } else {1426                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1427                }1428            },1429            CollectionMode::Fungible(_) => {1430                if let CreateItemData::Fungible(_) = data {1431                } else {1432                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1433                }1434            },1435            CollectionMode::ReFungible(_) => {1436                if let CreateItemData::ReFungible(data) = data {14371438                    // check sizes1439                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1440                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1441                } else {1442                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1443                }1444            },1445            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1446        };14471448        Ok(())1449    }14501451    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1452        match data1453        {1454            CreateItemData::NFT(data) => {1455                let item = NftItemType {1456                    collection: collection_id,1457                    owner,1458                    const_data: data.const_data,1459                    variable_data: data.variable_data1460                };14611462                Self::add_nft_item(item)?;1463            },1464            CreateItemData::Fungible(_) => {1465                let item = FungibleItemType {1466                    collection: collection_id,1467                    owner,1468                    value: (10 as u128).pow(collection.decimal_points as u32)1469                };14701471                Self::add_fungible_item(item)?;1472            },1473            CreateItemData::ReFungible(data) => {1474                let mut owner_list = Vec::new();1475                let value = (10 as u128).pow(collection.decimal_points as u32);1476                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14771478                let item = ReFungibleItemType {1479                    collection: collection_id,1480                    owner: owner_list,1481                    const_data: data.const_data,1482                    variable_data: data.variable_data1483                };14841485                Self::add_refungible_item(item)?;1486            }1487        };148814891490        // call event1491        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14921493        Ok(())1494    }14951496    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1497        let current_index = <ItemListIndex>::get(item.collection)1498            .checked_add(1)1499            .ok_or(Error::<T>::NumOverflow)?;1500        let itemcopy = item.clone();1501        let owner = item.owner.clone();15021503        Self::add_token_index(item.collection, current_index, owner.clone())?;15041505        <ItemListIndex>::insert(item.collection, current_index);1506        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15071508        // Add current block1509        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1510        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1511        1512        // Update balance1513        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1514            .checked_add(item.value)1515            .ok_or(Error::<T>::NumOverflow)?;1516        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15171518        Ok(())1519    }15201521    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1522        let current_index = <ItemListIndex>::get(item.collection)1523            .checked_add(1)1524            .ok_or(Error::<T>::NumOverflow)?;1525        let itemcopy = item.clone();15261527        let value = item.owner.first().unwrap().fraction;1528        let owner = item.owner.first().unwrap().owner.clone();15291530        Self::add_token_index(item.collection, current_index, owner.clone())?;15311532        <ItemListIndex>::insert(item.collection, current_index);1533        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15341535        // Add current block1536        let block_number: T::BlockNumber = 0.into();1537        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15381539        // Update balance1540        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1541            .checked_add(value)1542            .ok_or(Error::<T>::NumOverflow)?;1543        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15441545        Ok(())1546    }15471548    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1549        let current_index = <ItemListIndex>::get(item.collection)1550            .checked_add(1)1551            .ok_or(Error::<T>::NumOverflow)?;15521553        let item_owner = item.owner.clone();1554        let collection_id = item.collection.clone();1555        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15561557        <ItemListIndex>::insert(collection_id, current_index);1558        <NftItemList<T>>::insert(collection_id, current_index, item);15591560        // Add current block1561        let block_number: T::BlockNumber = 0.into();1562        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15631564        // Update balance1565        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1566            .checked_add(1)1567            .ok_or(Error::<T>::NumOverflow)?;1568        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15691570        Ok(())1571    }15721573    fn burn_refungible_item(1574        collection_id: CollectionId,1575        item_id: TokenId,1576        owner: T::AccountId,1577    ) -> DispatchResult {1578        ensure!(1579            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1580            Error::<T>::TokenNotFound1581        );1582        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1583        let item = collection1584            .owner1585            .iter()1586            .filter(|&i| i.owner == owner)1587            .next()1588            .unwrap();1589        Self::remove_token_index(collection_id, item_id, owner.clone())?;15901591        // remove approve list1592        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15931594        // update balance1595        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1596            .checked_sub(item.fraction)1597            .ok_or(Error::<T>::NumOverflow)?;1598        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15991600        <ReFungibleItemList<T>>::remove(collection_id, item_id);16011602        Ok(())1603    }16041605    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1606        ensure!(1607            <NftItemList<T>>::contains_key(collection_id, item_id),1608            Error::<T>::TokenNotFound1609        );1610        let item = <NftItemList<T>>::get(collection_id, item_id);1611        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16121613        // remove approve list1614        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16151616        // update balance1617        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1618            .checked_sub(1)1619            .ok_or(Error::<T>::NumOverflow)?;1620        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1621        <NftItemList<T>>::remove(collection_id, item_id);16221623        Ok(())1624    }16251626    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1627        ensure!(1628            <FungibleItemList<T>>::contains_key(collection_id, item_id),1629            Error::<T>::TokenNotFound1630        );1631        let item = <FungibleItemList<T>>::get(collection_id, item_id);1632        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16331634        // remove approve list1635        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16361637        // update balance1638        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1639            .checked_sub(item.value)1640            .ok_or(Error::<T>::NumOverflow)?;1641        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16421643        <FungibleItemList<T>>::remove(collection_id, item_id);16441645        Ok(())1646    }16471648    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1649        ensure!(1650            <Collection<T>>::contains_key(collection_id),1651            Error::<T>::CollectionNotFound1652        );1653        Ok(())1654    }16551656    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1657        Self::collection_exists(collection_id)?;16581659        let target_collection = <Collection<T>>::get(collection_id);1660        ensure!(1661            subject == target_collection.owner,1662            Error::<T>::NoPermission1663        );16641665        Ok(())1666    }16671668    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1669        let target_collection = <Collection<T>>::get(collection_id);1670        let mut result: bool = subject == target_collection.owner;1671        let exists = <AdminList<T>>::contains_key(collection_id);16721673        if !result & exists {1674            if <AdminList<T>>::get(collection_id).contains(&subject) {1675                result = true1676            }1677        }16781679        result1680    }16811682    fn check_owner_or_admin_permissions(1683        collection_id: CollectionId,1684        subject: T::AccountId,1685    ) -> DispatchResult {1686        Self::collection_exists(collection_id)?;1687        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16881689        ensure!(1690            result,1691            Error::<T>::NoPermission1692        );1693        Ok(())1694    }16951696    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1697        let target_collection = <Collection<T>>::get(collection_id);16981699        match target_collection.mode {1700            CollectionMode::NFT => {1701                <NftItemList<T>>::get(collection_id, item_id).owner == subject1702            }1703            CollectionMode::Fungible(_) => {1704                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1705            }1706            CollectionMode::ReFungible(_) => {1707                <ReFungibleItemList<T>>::get(collection_id, item_id)1708                    .owner1709                    .iter()1710                    .any(|i| i.owner == subject)1711            }1712            CollectionMode::Invalid => false,1713        }1714    }17151716    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1717        let mes = Error::<T>::AddresNotInWhiteList;1718        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1719        let wl = <WhiteList<T>>::get(collection_id);1720        ensure!(wl.contains(address), mes);17211722        Ok(())1723    }17241725    fn transfer_fungible(1726        collection_id: CollectionId,1727        item_id: TokenId,1728        value: u128,1729        owner: T::AccountId,1730        new_owner: T::AccountId,1731    ) -> DispatchResult {1732        ensure!(1733            <FungibleItemList<T>>::contains_key(collection_id, item_id),1734            Error::<T>::TokenNotFound1735        );17361737        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1738        let amount = full_item.value;17391740        ensure!(amount >= value, Error::<T>::TokenValueTooLow);17411742        // update balance1743        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1744            .checked_sub(value)1745            .ok_or(Error::<T>::NumOverflow)?;1746        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17471748        let mut new_owner_account_id = 0;1749        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1750        if new_owner_items.len() > 0 {1751            new_owner_account_id = new_owner_items[0];1752        }17531754        // transfer1755        if amount == value && new_owner_account_id == 0 {1756            // change owner1757            // new owner do not have account1758            let mut new_full_item = full_item.clone();1759            new_full_item.owner = new_owner.clone();1760            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17611762            // update balance1763            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1764                .checked_add(value)1765                .ok_or(Error::<T>::NumOverflow)?;1766            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17671768            // update index collection1769            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1770        } else {1771            let mut new_full_item = full_item.clone();1772            new_full_item.value -= value;17731774            // separate amount1775            if new_owner_account_id > 0 {1776                // new owner has account1777                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1778                item.value += value;17791780                // update balance1781                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1782                    .checked_add(value)1783                    .ok_or(Error::<T>::NumOverflow)?;1784                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17851786                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1787            } else {1788                // new owner do not have account1789                let item = FungibleItemType {1790                    collection: collection_id,1791                    owner: new_owner.clone(),1792                    value1793                };17941795                Self::add_fungible_item(item)?;1796            }17971798            if amount == value {1799                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18001801                // remove approve list1802                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1803                <FungibleItemList<T>>::remove(collection_id, item_id);1804            }18051806            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1807        }18081809        Ok(())1810    }18111812    fn transfer_refungible(1813        collection_id: CollectionId,1814        item_id: TokenId,1815        value: u128,1816        owner: T::AccountId,1817        new_owner: T::AccountId,1818    ) -> DispatchResult {1819        ensure!(1820            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1821            Error::<T>::TokenNotFound1822        );18231824        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1825        let item = full_item1826            .owner1827            .iter()1828            .filter(|i| i.owner == owner)1829            .next()1830            .ok_or(Error::<T>::NumOverflow)?;1831        let amount = item.fraction;18321833        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18341835        // update balance1836        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1837            .checked_sub(value)1838            .ok_or(Error::<T>::NumOverflow)?;1839        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18401841        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1842            .checked_add(value)1843            .ok_or(Error::<T>::NumOverflow)?;1844        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18451846        let old_owner = item.owner.clone();1847        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);18481849        // transfer1850        if amount == value && !new_owner_has_account {1851            // change owner1852            // new owner do not have account1853            let mut new_full_item = full_item.clone();1854            new_full_item1855                .owner1856                .iter_mut()1857                .find(|i| i.owner == owner)1858                .unwrap()1859                .owner = new_owner.clone();1860            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18611862            // update index collection1863            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1864        } else {1865            let mut new_full_item = full_item.clone();1866            new_full_item1867                .owner1868                .iter_mut()1869                .find(|i| i.owner == owner)1870                .unwrap()1871                .fraction -= value;18721873            // separate amount1874            if new_owner_has_account {1875                // new owner has account1876                new_full_item1877                    .owner1878                    .iter_mut()1879                    .find(|i| i.owner == new_owner)1880                    .unwrap()1881                    .fraction += value;1882            } else {1883                // new owner do not have account1884                new_full_item.owner.push(Ownership {1885                    owner: new_owner.clone(),1886                    fraction: value,1887                });1888                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1889            }18901891            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1892        }18931894        Ok(())1895    }18961897    fn transfer_nft(1898        collection_id: CollectionId,1899        item_id: TokenId,1900        sender: T::AccountId,1901        new_owner: T::AccountId,1902    ) -> DispatchResult {1903        ensure!(1904            <NftItemList<T>>::contains_key(collection_id, item_id),1905            Error::<T>::TokenNotFound1906        );19071908        let mut item = <NftItemList<T>>::get(collection_id, item_id);19091910        ensure!(1911            sender == item.owner,1912            Error::<T>::MustBeTokenOwner1913        );19141915        // update balance1916        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1917            .checked_sub(1)1918            .ok_or(Error::<T>::NumOverflow)?;1919        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19201921        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1922            .checked_add(1)1923            .ok_or(Error::<T>::NumOverflow)?;1924        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19251926        // change owner1927        let old_owner = item.owner.clone();1928        item.owner = new_owner.clone();1929        <NftItemList<T>>::insert(collection_id, item_id, item);19301931        // update index collection1932        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19331934        // reset approved list1935        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1936        Ok(())1937    }1938    1939    fn item_exists(1940        collection_id: CollectionId,1941        item_id: TokenId,1942        mode: &CollectionMode1943    ) -> DispatchResult {1944        match mode {1945            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1946            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1947            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1948            _ => ()1949        };1950        1951        Ok(())1952    }19531954    fn set_re_fungible_variable_data(1955        collection_id: CollectionId,1956        item_id: TokenId,1957        data: Vec<u8>1958    ) -> DispatchResult {1959        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19601961        item.variable_data = data;19621963        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19641965        Ok(())1966    }19671968    fn set_nft_variable_data(1969        collection_id: CollectionId,1970        item_id: TokenId,1971        data: Vec<u8>1972    ) -> DispatchResult {1973        let mut item = <NftItemList<T>>::get(collection_id, item_id);1974        1975        item.variable_data = data;19761977        <NftItemList<T>>::insert(collection_id, item_id, item);1978        1979        Ok(())1980    }19811982    fn init_collection(item: &CollectionType<T::AccountId>) {1983        // check params1984        assert!(1985            item.decimal_points <= MAX_DECIMAL_POINTS,1986            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"1987        );1988        assert!(1989            item.name.len() <= 64,1990            "Collection name can not be longer than 63 char"1991        );1992        assert!(1993            item.name.len() <= 256,1994            "Collection description can not be longer than 255 char"1995        );1996        assert!(1997            item.token_prefix.len() <= 16,1998            "Token prefix can not be longer than 15 char"1999        );20002001        // Generate next collection ID2002        let next_id = CreatedCollectionCount::get()2003            .checked_add(1)2004            .unwrap();20052006        CreatedCollectionCount::put(next_id);2007    }20082009    fn init_nft_token(item: &NftItemType<T::AccountId>) {2010        let current_index = <ItemListIndex>::get(item.collection)2011            .checked_add(1)2012            .unwrap();20132014        let item_owner = item.owner.clone();2015        let collection_id = item.collection.clone();2016        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20172018        <ItemListIndex>::insert(collection_id, current_index);20192020        // Update balance2021        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2022            .checked_add(1)2023            .unwrap();2024        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2025    }20262027    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2028        let current_index = <ItemListIndex>::get(item.collection)2029            .checked_add(1)2030            .unwrap();2031        let owner = item.owner.clone();20322033        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20342035        <ItemListIndex>::insert(item.collection, current_index);20362037        // Update balance2038        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2039            .checked_add(item.value)2040            .unwrap();2041        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2042    }20432044    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2045        let current_index = <ItemListIndex>::get(item.collection)2046            .checked_add(1)2047            .unwrap();20482049        let value = item.owner.first().unwrap().fraction;2050        let owner = item.owner.first().unwrap().owner.clone();20512052        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20532054        <ItemListIndex>::insert(item.collection, current_index);20552056        // Update balance2057        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2058            .checked_add(value)2059            .unwrap();2060        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2061    }20622063    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {20642065        // add to account limit2066        if <AccountItemCount<T>>::contains_key(owner.clone()) {20672068            // bound Owned tokens by a single address2069            let count = <AccountItemCount<T>>::get(owner.clone());2070            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20712072            <AccountItemCount<T>>::insert(owner.clone(), count2073                .checked_add(1)2074                .ok_or(Error::<T>::NumOverflow)?);2075        }2076        else {2077            <AccountItemCount<T>>::insert(owner.clone(), 1);2078        }20792080        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2081        if list_exists {2082            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2083            let item_contains = list.contains(&item_index.clone());20842085            if !item_contains {2086                list.push(item_index.clone());2087            }20882089            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2090        } else {2091            let mut itm = Vec::new();2092            itm.push(item_index.clone());2093            <AddressTokens<T>>::insert(collection_id, owner, itm);2094            2095        }20962097        Ok(())2098    }20992100    fn remove_token_index(2101        collection_id: CollectionId,2102        item_index: TokenId,2103        owner: T::AccountId,2104    ) -> DispatchResult {21052106        // update counter2107        <AccountItemCount<T>>::insert(owner.clone(), 2108            <AccountItemCount<T>>::get(owner.clone())2109            .checked_sub(1)2110            .ok_or(Error::<T>::NumOverflow)?);211121122113        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2114        if list_exists {2115            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2116            let item_contains = list.contains(&item_index.clone());21172118            if item_contains {2119                list.retain(|&item| item != item_index);2120                <AddressTokens<T>>::insert(collection_id, owner, list);2121            }2122        }21232124        Ok(())2125    }21262127    fn move_token_index(2128        collection_id: CollectionId,2129        item_index: TokenId,2130        old_owner: T::AccountId,2131        new_owner: T::AccountId,2132    ) -> DispatchResult {2133        Self::remove_token_index(collection_id, item_index, old_owner)?;2134        Self::add_token_index(collection_id, item_index, new_owner)?;21352136        Ok(())2137    }2138}21392140////////////////////////////////////////////////////////////////////////////////////////////////////2141// Economic models2142// #region21432144/// Fee multiplier.2145pub type Multiplier = FixedU128;21462147type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2148    <T as system::Trait>::AccountId,2149>>::Balance;2150type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2151    <T as system::Trait>::AccountId,2152>>::NegativeImbalance;21532154/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2155/// in the queue.2156#[derive(Encode, Decode, Clone, Eq, PartialEq)]2157pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2158    #[codec(compact)] BalanceOf<T>2159);21602161impl<T: Trait + Send + Sync> sp_std::fmt::Debug2162    for ChargeTransactionPayment<T>2163{2164    #[cfg(feature = "std")]2165    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2166        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2167    }2168    #[cfg(not(feature = "std"))]2169    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2170        Ok(())2171    }2172}21732174impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2175where2176    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2177    BalanceOf<T>: Send + Sync + FixedPointOperand,2178{2179    /// utility constructor. Used only in client/factory code.2180    pub fn from(fee: BalanceOf<T>) -> Self {2181        Self(fee)2182    }21832184    pub fn traditional_fee(2185        len: usize,2186        info: &DispatchInfoOf<T::Call>,2187        tip: BalanceOf<T>,2188    ) -> BalanceOf<T>2189    where2190        T::Call: Dispatchable<Info = DispatchInfo>,2191    {2192        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2193    }21942195    fn withdraw_fee(2196        &self,2197        who: &T::AccountId,2198        call: &T::Call,2199        info: &DispatchInfoOf<T::Call>,2200        len: usize,2201    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2202        let tip = self.0;22032204        // Set fee based on call type. Creating collection costs 1 Unique.2205        // All other transactions have traditional fees so far2206        // let fee = match call.is_sub_type() {2207        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2208        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2209        //                                                 // _ => <BalanceOf<T>>::from(100)2210        // };2211        let fee = Self::traditional_fee(len, info, tip);22122213        // Determine who is paying transaction fee based on ecnomic model2214        // Parse call to extract collection ID and access collection sponsor2215        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2216            Some(Call::create_item(collection_id, _properties, _owner)) => {2217                <Collection<T>>::get(collection_id).sponsor2218            }2219            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2220                let _collection_mode = <Collection<T>>::get(collection_id).mode;22212222                // sponsor timeout2223                let sponsor_transfer = match _collection_mode {2224                    CollectionMode::NFT => {2225                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2226                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2227                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2228                        if block_number >= limit_time {2229                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2230                            true2231                        }2232                        else {2233                            false2234                        }2235                    }2236                    CollectionMode::Fungible(_) => {2237                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2238                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2239                        if basket.iter().any(|i| i.address == _new_owner.clone())2240                        {2241                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2242                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2243                            if block_number >= limit_time {2244                                basket.retain(|x| x.address == item.address);2245                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2246                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2247                                true2248                            }2249                            else {2250                                false2251                            }2252                        }2253                        else {2254                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2255                            true2256                        }2257                    }2258                    CollectionMode::ReFungible(_) => {2259                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2260                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2261                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2262                        if block_number >= limit_time {2263                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2264                            true2265                        } else {2266                            false2267                        }2268                    }2269                    _ => {2270                        false2271                    },2272                };22732274                if !sponsor_transfer {2275                    T::AccountId::default()2276                } else {2277                    <Collection<T>>::get(collection_id).sponsor2278                }2279            }22802281            _ => T::AccountId::default(),2282        };22832284        // Sponsor smart contracts2285        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22862287            // On instantiation: set the contract owner2288            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22892290                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2291                    code_hash,2292                    &data,2293                    &who,2294                );2295                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22962297                T::AccountId::default()2298            },22992300            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2301            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23022303                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23042305                let mut sponsor_transfer = false;2306                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2307                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2308                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2309                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2310                    let limit_time = last_tx_block + rate_limit;23112312                    if block_number >= limit_time {2313                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2314                        sponsor_transfer = true;2315                    }2316                } else {2317                    sponsor_transfer = false;2318                }2319               2320                2321                let mut sp = T::AccountId::default();2322                if sponsor_transfer {2323                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2324                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2325                            sp = called_contract;2326                        }2327                    }2328                }23292330                sp2331            },23322333            _ => sponsor,2334        };23352336        let mut who_pays_fee: T::AccountId = sponsor.clone();2337        if sponsor == T::AccountId::default() {2338            who_pays_fee = who.clone();2339        }23402341        // Only mess with balances if fee is not zero.2342        if fee.is_zero() {2343            return Ok((fee, None));2344        }23452346        match <T as transaction_payment::Trait>::Currency::withdraw(2347            &who_pays_fee,2348            fee,2349            if tip.is_zero() {2350                WithdrawReason::TransactionPayment.into()2351            } else {2352                WithdrawReason::TransactionPayment | WithdrawReason::Tip2353            },2354            ExistenceRequirement::KeepAlive,2355        ) {2356            Ok(imbalance) => Ok((fee, Some(imbalance))),2357            Err(_) => Err(InvalidTransaction::Payment.into()),2358        }2359    }2360}236123622363impl<T: Trait + Send + Sync> SignedExtension2364    for ChargeTransactionPayment<T>2365where2366    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2367    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2368{2369    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2370    type AccountId = T::AccountId;2371    type Call = T::Call;2372    type AdditionalSigned = ();2373    type Pre = (2374        BalanceOf<T>,2375        Self::AccountId,2376        Option<NegativeImbalanceOf<T>>,2377        BalanceOf<T>,2378    );2379    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2380        Ok(())2381    }23822383    fn validate(2384        &self,2385        _who: &Self::AccountId,2386        _call: &Self::Call,2387        _info: &DispatchInfoOf<Self::Call>,2388        _len: usize,2389    ) -> TransactionValidity {2390        Ok(ValidTransaction::default())2391    }23922393    fn pre_dispatch(2394        self,2395        who: &Self::AccountId,2396        call: &Self::Call,2397        info: &DispatchInfoOf<Self::Call>,2398        len: usize,2399    ) -> Result<Self::Pre, TransactionValidityError> {2400        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2401        Ok((self.0, who.clone(), imbalance, fee))2402    }24032404    fn post_dispatch(2405        pre: Self::Pre,2406        info: &DispatchInfoOf<Self::Call>,2407        post_info: &PostDispatchInfoOf<Self::Call>,2408        len: usize,2409        _result: &DispatchResult,2410    ) -> Result<(), TransactionValidityError> {2411        let (tip, who, imbalance, fee) = pre;2412        if let Some(payed) = imbalance {2413            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2414                len as u32, info, post_info, tip,2415            );2416            let refund = fee.saturating_sub(actual_fee);2417            let actual_payment =2418                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2419                    &who, refund,2420                ) {2421                    Ok(refund_imbalance) => {2422                        // The refund cannot be larger than the up front payed max weight.2423                        // `PostDispatchInfo::calc_unspent` guards against such a case.2424                        match payed.offset(refund_imbalance) {2425                            Ok(actual_payment) => actual_payment,2426                            Err(_) => return Err(InvalidTransaction::Payment.into()),2427                        }2428                    }2429                    // We do not recreate the account using the refund. The up front payment2430                    // is gone in that case.2431                    Err(_) => payed,2432                };2433            let imbalances = actual_payment.split(tip);2434            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2435                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2436            );2437        }2438        Ok(())2439    }2440}24412442// #endregion