git.delta.rocks / unique-network / refs/commits / 68e2fd36f1cb

difftreelog

source

pallets/nft/src/lib.rs87.3 KiBsourcehistory
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;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263264decl_error! {265	/// Error for non-fungible-token module.266	pub enum Error for Module<T: Trait> {267        /// Total collections bound exceeded268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist278        CollectionNotFound,279        /// Item not exists280        TokenNotFound,281        /// Arithmetic calculation overflow282        NumOverflow,       283        /// Account already has admin role284        AlreadyAdmin,  285        /// You do not own this collection286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal292        MustBeTokenOwner,293        /// Item balance not enouth294        TokenValueTooLow,295        /// Size of item is too large296        NftSizeLimitExceeded,297        /// Size of item must be 0 with fungible type298        FungibleUnexpectedParam,299        /// No approve found300        ApproveNotFound,301        /// Requested value more than approved302        TokenValueNotEnough,303        /// Only approved addresses can call this method304        ApproveRequired,305        /// Address is not in white list306        AddresNotInWhiteList,307        /// Number of collection admins bound exceeded308        CollectionAdminsLimitExceeded,309        /// Owned tokens by a single address bound exceeded310        AddressOwnershipLimitExceeded,311        /// Length of items properties must be greater than 0312        EmptyArgument,313	}314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319    /// Weight information for extrinsics in this pallet.320	type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326// #endregion327328decl_storage! {329    trait Store for Module<T: Trait> as Nft {330331        // Private members332        NextCollectionID: u64;333        CreatedCollectionCount: u64;334        ChainVersion: u64;335        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337        // Chain limits struct338        pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340        // Bound counters341        CollectionCount: u64;342        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344        // Basic collections345        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349        /// Balance owner per collection map350        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352        /// second parameter: item id + owner account id353        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355        /// Item collections356        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360        /// Index list361        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363        /// Tokens transfer baskets364        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368        // Contract Sponsorship and Ownership369        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;370        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;371        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;372        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;373    }374    add_extra_genesis {375        build(|config: &GenesisConfig<T>| {376            // Modification of storage377            for (_num, _c) in &config.collection {378                <Module<T>>::init_collection(_c);379            }380381            for (_num, _q, _i) in &config.nft_item_id {382                <Module<T>>::init_nft_token(_i);383            }384385            for (_num, _q, _i) in &config.fungible_item_id {386                <Module<T>>::init_fungible_token(_i);387            }388389            for (_num, _q, _i) in &config.refungible_item_id {390                <Module<T>>::init_refungible_token(_i);391            }392        })393    }394}395396decl_event!(397    pub enum Event<T>398    where399        AccountId = <T as system::Trait>::AccountId,400    {401        /// New collection was created402        /// 403        /// # Arguments404        /// 405        /// * collection_id: Globally unique identifier of newly created collection.406        /// 407        /// * mode: [CollectionMode] converted into u8.408        /// 409        /// * account_id: Collection owner.410        Created(u64, u8, AccountId),411412        /// New item was created.413        /// 414        /// # Arguments415        /// 416        /// * collection_id: Id of the collection where item was created.417        /// 418        /// * item_id: Id of an item. Unique within the collection.419        ItemCreated(u64, u64),420421        /// Collection item was burned.422        /// 423        /// # Arguments424        /// 425        /// collection_id.426        /// 427        /// item_id: Identifier of burned NFT.428        ItemDestroyed(u64, u64),429    }430);431432decl_module! {433    pub struct Module<T: Trait> for enum Call where origin: T::Origin {434435        fn deposit_event() = default;436        type Error = Error<T>;437438        fn on_initialize(now: T::BlockNumber) -> Weight {439440            if ChainVersion::get() < 2441            {442                let value = NextCollectionID::get();443                CreatedCollectionCount::put(value);444                ChainVersion::put(2);445            }446447            0448        }449450        /// 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.451        /// 452        /// # Permissions453        /// 454        /// * Anyone.455        /// 456        /// # Arguments457        /// 458        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.459        /// 460        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.461        /// 462        /// * token_prefix: UTF-8 string with token prefix.463        /// 464        /// * mode: [CollectionMode] collection type and type dependent data.465        // returns collection ID466        #[weight = T::WeightInfo::create_collection()]467        pub fn create_collection(origin,468                                 collection_name: Vec<u16>,469                                 collection_description: Vec<u16>,470                                 token_prefix: Vec<u8>,471                                 mode: CollectionMode) -> DispatchResult {472473            // Anyone can create a collection474            let who = ensure_signed(origin)?;475476            let decimal_points = match mode {477                CollectionMode::Fungible(points) => points,478                CollectionMode::ReFungible(points) => points,479                _ => 0480            };481482            // bound Total number of collections483            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);484485            // check params486            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);487488            let mut name = collection_name.to_vec();489            name.push(0);490            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);491492            let mut description = collection_description.to_vec();493            description.push(0);494            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);495496            let mut prefix = token_prefix.to_vec();497            prefix.push(0);498            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);499500            // Generate next collection ID501            let next_id = CreatedCollectionCount::get()502                .checked_add(1)503                .ok_or(Error::<T>::NumOverflow)?;504505            // bound counter506            let total = CollectionCount::get()507                .checked_add(1)508                .ok_or(Error::<T>::NumOverflow)?;509510            CreatedCollectionCount::put(next_id);511            CollectionCount::put(total);512513            // Create new collection514            let new_collection = CollectionType {515                owner: who.clone(),516                name: name,517                mode: mode.clone(),518                mint_mode: false,519                access: AccessMode::Normal,520                description: description,521                decimal_points: decimal_points,522                token_prefix: prefix,523                offchain_schema: Vec::new(),524                sponsor: T::AccountId::default(),525                unconfirmed_sponsor: T::AccountId::default(),526                variable_on_chain_schema: Vec::new(),527                const_on_chain_schema: Vec::new(),528            };529530            // Add new collection to map531            <Collection<T>>::insert(next_id, new_collection);532533            // call event534            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));535536            Ok(())537        }538539        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.540        /// 541        /// # Permissions542        /// 543        /// * Collection Owner.544        /// 545        /// # Arguments546        /// 547        /// * collection_id: collection to destroy.548        #[weight = T::WeightInfo::destroy_collection()]549        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {550551            let sender = ensure_signed(origin)?;552            Self::check_owner_permissions(collection_id, sender)?;553554            <AddressTokens<T>>::remove_prefix(collection_id);555            <ApprovedList<T>>::remove_prefix(collection_id);556            <Balance<T>>::remove_prefix(collection_id);557            <ItemListIndex>::remove(collection_id);558            <AdminList<T>>::remove(collection_id);559            <Collection<T>>::remove(collection_id);560            <WhiteList<T>>::remove(collection_id);561562            <NftItemList<T>>::remove_prefix(collection_id);563            <FungibleItemList<T>>::remove_prefix(collection_id);564            <ReFungibleItemList<T>>::remove_prefix(collection_id);565566            <NftTransferBasket<T>>::remove_prefix(collection_id);567            <FungibleTransferBasket<T>>::remove_prefix(collection_id);568            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);569570            if CollectionCount::get() > 0571            {572                // bound couter573                let total = CollectionCount::get()574                    .checked_sub(1)575                    .ok_or(Error::<T>::NumOverflow)?;576577                CollectionCount::put(total);578            }579580            Ok(())581        }582583        /// Add an address to white list.584        /// 585        /// # Permissions586        /// 587        /// * Collection Owner588        /// * Collection Admin589        /// 590        /// # Arguments591        /// 592        /// * collection_id.593        /// 594        /// * address.595        #[weight = T::WeightInfo::add_to_white_list()]596        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{597598            let sender = ensure_signed(origin)?;599            Self::check_owner_or_admin_permissions(collection_id, sender)?;600601            let mut white_list_collection: Vec<T::AccountId>;602            if <WhiteList<T>>::contains_key(collection_id) {603                white_list_collection = <WhiteList<T>>::get(collection_id);604                if !white_list_collection.contains(&address.clone())605                {606                    white_list_collection.push(address.clone());607                }608            }609            else {610                white_list_collection = Vec::new();611                white_list_collection.push(address.clone());612            }613614            <WhiteList<T>>::insert(collection_id, white_list_collection);615            Ok(())616        }617618        /// Remove an address from white list.619        /// 620        /// # Permissions621        /// 622        /// * Collection Owner623        /// * Collection Admin624        /// 625        /// # Arguments626        /// 627        /// * collection_id.628        /// 629        /// * address.630        #[weight = T::WeightInfo::remove_from_white_list()]631        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{632633            let sender = ensure_signed(origin)?;634            Self::check_owner_or_admin_permissions(collection_id, sender)?;635636            if <WhiteList<T>>::contains_key(collection_id) {637                let mut white_list_collection = <WhiteList<T>>::get(collection_id);638                if white_list_collection.contains(&address.clone())639                {640                    white_list_collection.retain(|i| *i != address.clone());641                    <WhiteList<T>>::insert(collection_id, white_list_collection);642                }643            }644645            Ok(())646        }647648        /// Toggle between normal and white list access for the methods with access for `Anyone`.649        /// 650        /// # Permissions651        /// 652        /// * Collection Owner.653        /// 654        /// # Arguments655        /// 656        /// * collection_id.657        /// 658        /// * mode: [AccessMode]659        #[weight = T::WeightInfo::set_public_access_mode()]660        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult661        {662            let sender = ensure_signed(origin)?;663664            Self::check_owner_permissions(collection_id, sender)?;665            let mut target_collection = <Collection<T>>::get(collection_id);666            target_collection.access = mode;667            <Collection<T>>::insert(collection_id, target_collection);668669            Ok(())670        }671672        /// Allows Anyone to create tokens if:673        /// * White List is enabled, and674        /// * Address is added to white list, and675        /// * This method was called with True parameter676        /// 677        /// # Permissions678        /// * Collection Owner679        ///680        /// # Arguments681        /// 682        /// * collection_id.683        /// 684        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.685        #[weight = T::WeightInfo::set_mint_permission()]686        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult687        {688            let sender = ensure_signed(origin)?;689690            Self::check_owner_permissions(collection_id, sender)?;691            let mut target_collection = <Collection<T>>::get(collection_id);692            target_collection.mint_mode = mint_permission;693            <Collection<T>>::insert(collection_id, target_collection);694695            Ok(())696        }697698        /// Change the owner of the collection.699        /// 700        /// # Permissions701        /// 702        /// * Collection Owner.703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * new_owner.709        #[weight = T::WeightInfo::change_collection_owner()]710        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {711712            let sender = ensure_signed(origin)?;713            Self::check_owner_permissions(collection_id, sender)?;714            let mut target_collection = <Collection<T>>::get(collection_id);715            target_collection.owner = new_owner;716            <Collection<T>>::insert(collection_id, target_collection);717718            Ok(())719        }720721        /// Adds an admin of the Collection.722        /// 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. 723        /// 724        /// # Permissions725        /// 726        /// * Collection Owner.727        /// * Collection Admin.728        /// 729        /// # Arguments730        /// 731        /// * collection_id: ID of the Collection to add admin for.732        /// 733        /// * new_admin_id: Address of new admin to add.734        #[weight = T::WeightInfo::add_collection_admin()]735        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {736737            let sender = ensure_signed(origin)?;738            Self::check_owner_or_admin_permissions(collection_id, sender)?;739            let mut admin_arr: Vec<T::AccountId> = Vec::new();740741            if <AdminList<T>>::contains_key(collection_id)742            {743                admin_arr = <AdminList<T>>::get(collection_id);744                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);745            }746747            // Number of collection admins748            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);749750            admin_arr.push(new_admin_id);751            <AdminList<T>>::insert(collection_id, admin_arr);752753            Ok(())754        }755756        /// 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.757        ///758        /// # Permissions759        /// 760        /// * Collection Owner.761        /// * Collection Admin.762        /// 763        /// # Arguments764        /// 765        /// * collection_id: ID of the Collection to remove admin for.766        /// 767        /// * account_id: Address of admin to remove.768        #[weight = T::WeightInfo::remove_collection_admin()]769        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {770771            let sender = ensure_signed(origin)?;772            Self::check_owner_or_admin_permissions(collection_id, sender)?;773774            if <AdminList<T>>::contains_key(collection_id)775            {776                let mut admin_arr = <AdminList<T>>::get(collection_id);777                admin_arr.retain(|i| *i != account_id);778                <AdminList<T>>::insert(collection_id, admin_arr);779            }780781            Ok(())782        }783784        /// # Permissions785        /// 786        /// * Collection Owner787        /// 788        /// # Arguments789        /// 790        /// * collection_id.791        /// 792        /// * new_sponsor.793        #[weight = T::WeightInfo::set_collection_sponsor()]794        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {795796            let sender = ensure_signed(origin)?;797            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);798799            let mut target_collection = <Collection<T>>::get(collection_id);800            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);801802            target_collection.unconfirmed_sponsor = new_sponsor;803            <Collection<T>>::insert(collection_id, target_collection);804805            Ok(())806        }807808        /// # Permissions809        /// 810        /// * Sponsor.811        /// 812        /// # Arguments813        /// 814        /// * collection_id.815        #[weight = T::WeightInfo::confirm_sponsorship()]816        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {817818            let sender = ensure_signed(origin)?;819            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);820821            let mut target_collection = <Collection<T>>::get(collection_id);822            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);823824            target_collection.sponsor = target_collection.unconfirmed_sponsor;825            target_collection.unconfirmed_sponsor = T::AccountId::default();826            <Collection<T>>::insert(collection_id, target_collection);827828            Ok(())829        }830831        /// Switch back to pay-per-own-transaction model.832        ///833        /// # Permissions834        ///835        /// * Collection owner.836        /// 837        /// # Arguments838        /// 839        /// * collection_id.840        #[weight = T::WeightInfo::remove_collection_sponsor()]841        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {842843            let sender = ensure_signed(origin)?;844            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);845846            let mut target_collection = <Collection<T>>::get(collection_id);847            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);848849            target_collection.sponsor = T::AccountId::default();850            <Collection<T>>::insert(collection_id, target_collection);851852            Ok(())853        }854855        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.856        /// 857        /// # Permissions858        /// 859        /// * Collection Owner.860        /// * Collection Admin.861        /// * Anyone if862        ///     * White List is enabled, and863        ///     * Address is added to white list, and864        ///     * MintPermission is enabled (see SetMintPermission method)865        /// 866        /// # Arguments867        /// 868        /// * collection_id: ID of the collection.869        /// 870        /// * owner: Address, initial owner of the NFT.871        ///872        /// * data: Token data to store on chain.873        // #[weight =874        // (130_000_000 as Weight)875        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))876        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))877        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]878879        #[weight = T::WeightInfo::create_item(data.len())]880        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {881882            let sender = ensure_signed(origin)?;883884            Self::collection_exists(collection_id)?;885886            let target_collection = <Collection<T>>::get(collection_id);887888            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;889            Self::validate_create_item_args(&target_collection, &data)?;890            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;891892            Ok(())893        }894895        /// This method creates multiple instances of NFT Collection created with CreateCollection method.896        /// 897        /// # Permissions898        /// 899        /// * Collection Owner.900        /// * Collection Admin.901        /// * Anyone if902        ///     * White List is enabled, and903        ///     * Address is added to white list, and904        ///     * MintPermission is enabled (see SetMintPermission method)905        /// 906        /// # Arguments907        /// 908        /// * collection_id: ID of the collection.909        /// 910        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].911        /// 912        /// * owner: Address, initial owner of the NFT.913        #[weight = T::WeightInfo::create_item(items_data.into_iter()914                               .map(|data| { data.len() })915                               .sum())]916        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {917918            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);919            let sender = ensure_signed(origin)?;920921            Self::collection_exists(collection_id)?;922            let target_collection = <Collection<T>>::get(collection_id);923924            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;925926            for data in &items_data {927                Self::validate_create_item_args(&target_collection, data)?;928            }929            for data in &items_data {930                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;931            }932933            Ok(())934        }935936        /// Destroys a concrete instance of NFT.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner.941        /// * Collection Admin.942        /// * Current NFT Owner.943        /// 944        /// # Arguments945        /// 946        /// * collection_id: ID of the collection.947        /// 948        /// * item_id: ID of NFT to burn.949        #[weight = T::WeightInfo::burn_item()]950        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {951952            let sender = ensure_signed(origin)?;953            Self::collection_exists(collection_id)?;954955            // Transfer permissions check956            let target_collection = <Collection<T>>::get(collection_id);957            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||958                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),959                Error::<T>::NoPermission);960961            if target_collection.access == AccessMode::WhiteList {962                Self::check_white_list(collection_id, &sender)?;963            }964965            match target_collection.mode966            {967                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,968                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,969                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,970                _ => ()971            };972973            // call event974            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));975976            Ok(())977        }978979        /// Change ownership of the token.980        /// 981        /// # Permissions982        /// 983        /// * Collection Owner984        /// * Collection Admin985        /// * Current NFT owner986        ///987        /// # Arguments988        /// 989        /// * recipient: Address of token recipient.990        /// 991        /// * collection_id.992        /// 993        /// * item_id: ID of the item994        ///     * Non-Fungible Mode: Required.995        ///     * Fungible Mode: Ignored.996        ///     * Re-Fungible Mode: Required.997        /// 998        /// * value: Amount to transfer.999        ///     * Non-Fungible Mode: Ignored1000        ///     * Fungible Mode: Must specify transferred amount1001        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1002        #[weight = T::WeightInfo::transfer()]1003        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;10061007            // Transfer permissions check1008            let target_collection = <Collection<T>>::get(collection_id);1009            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1010                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1011                Error::<T>::NoPermission);10121013            if target_collection.access == AccessMode::WhiteList {1014                Self::check_white_list(collection_id, &sender)?;1015                Self::check_white_list(collection_id, &recipient)?;1016            }10171018            match target_collection.mode1019            {1020                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1021                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1022                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1023                _ => ()1024            };10251026            Ok(())1027        }10281029        /// Set, change, or remove approved address to transfer the ownership of the NFT.1030        /// 1031        /// # Permissions1032        /// 1033        /// * Collection Owner1034        /// * Collection Admin1035        /// * Current NFT owner1036        /// 1037        /// # Arguments1038        /// 1039        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1040        /// 1041        /// * collection_id.1042        /// 1043        /// * item_id: ID of the item.1044        #[weight = T::WeightInfo::approve()]1045        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10461047            let sender = ensure_signed(origin)?;10481049            // Transfer permissions check1050            let target_collection = <Collection<T>>::get(collection_id);1051            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1052                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1053                Error::<T>::NoPermission);10541055            if target_collection.access == AccessMode::WhiteList {1056                Self::check_white_list(collection_id, &sender)?;1057                Self::check_white_list(collection_id, &approved)?;1058            }10591060            // amount param stub1061            let amount = 100000000;10621063            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1064            if list_exists {10651066                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1067                let item_contains = list.iter().any(|i| i.approved == approved);10681069                if !item_contains {1070                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1071                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1072                }1073            } else {10741075                let mut list = Vec::new();1076                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1077                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1078            }10791080            Ok(())1081        }1082        1083        /// 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.1084        /// 1085        /// # Permissions1086        /// * Collection Owner1087        /// * Collection Admin1088        /// * Current NFT owner1089        /// * Address approved by current NFT owner1090        /// 1091        /// # Arguments1092        /// 1093        /// * from: Address that owns token.1094        /// 1095        /// * recipient: Address of token recipient.1096        /// 1097        /// * collection_id.1098        /// 1099        /// * item_id: ID of the item.1100        /// 1101        /// * value: Amount to transfer.1102        #[weight = T::WeightInfo::transfer_from()]1103        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11041105            let sender = ensure_signed(origin)?;1106            let mut appoved_transfer = false;11071108            // Check approve1109            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1110                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1111                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1112                if opt_item.is_some()1113                {1114                    appoved_transfer = true;1115                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1116                }1117            }11181119            // Transfer permissions check1120            let target_collection = <Collection<T>>::get(collection_id);1121                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1122                Error::<T>::NoPermission);11231124            if target_collection.access == AccessMode::WhiteList {1125                Self::check_white_list(collection_id, &sender)?;1126                Self::check_white_list(collection_id, &recipient)?;1127            }11281129            // remove approve1130            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1131                .into_iter().filter(|i| i.approved != sender.clone()).collect();1132            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113311341135            match target_collection.mode1136            {1137                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1138                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1139                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1140                _ => ()1141            };11421143            Ok(())1144        }11451146        ///1147        #[weight = 0]1148        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11491150            // let no_perm_mes = "You do not have permissions to modify this collection";1151            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1152            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1153            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11541155            // // on_nft_received  call11561157            // Self::transfer(origin, collection_id, item_id, new_owner)?;11581159            Ok(())1160        }11611162        /// Set off-chain data schema.1163        /// 1164        /// # Permissions1165        /// 1166        /// * Collection Owner1167        /// * Collection Admin1168        /// 1169        /// # Arguments1170        /// 1171        /// * collection_id.1172        /// 1173        /// * schema: String representing the offchain data schema.1174        #[weight = T::WeightInfo::set_variable_meta_data()]1175        pub fn set_variable_meta_data (1176            origin,1177            collection_id: u64,1178            item_id: u64,1179            data: Vec<u8>1180        ) -> DispatchResult {1181            let sender = ensure_signed(origin)?;1182            1183            Self::collection_exists(collection_id)?;11841185            // Modify permissions check1186            let target_collection = <Collection<T>>::get(collection_id);1187            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1188                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1189                Error::<T>::NoPermission);11901191            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11921193            match target_collection.mode1194            {1195                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1196                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1197                _ => ()1198            };11991200            Ok(())1201        }1202        12031204        /// Set off-chain data schema.1205        /// 1206        /// # Permissions1207        /// 1208        /// * Collection Owner1209        /// * Collection Admin1210        /// 1211        /// # Arguments1212        /// 1213        /// * collection_id.1214        /// 1215        /// * schema: String representing the offchain data schema.1216        #[weight = T::WeightInfo::set_offchain_schema()]1217        pub fn set_offchain_schema(1218            origin,1219            collection_id: u64,1220            schema: Vec<u8>1221        ) -> DispatchResult {1222            let sender = ensure_signed(origin)?;1223            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12241225            let mut target_collection = <Collection<T>>::get(collection_id);1226            target_collection.offchain_schema = schema;1227            <Collection<T>>::insert(collection_id, target_collection);12281229            Ok(())1230        }12311232        /// Set const on-chain data schema.1233        /// 1234        /// # Permissions1235        /// 1236        /// * Collection Owner1237        /// * Collection Admin1238        /// 1239        /// # Arguments1240        /// 1241        /// * collection_id.1242        /// 1243        /// * schema: String representing the const on-chain data schema.1244        #[weight = T::WeightInfo::set_const_on_chain_schema()]1245        pub fn set_const_on_chain_schema (1246            origin,1247            collection_id: u64,1248            schema: Vec<u8>1249        ) -> DispatchResult {1250            let sender = ensure_signed(origin)?;1251            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12521253            let mut target_collection = <Collection<T>>::get(collection_id);1254            target_collection.const_on_chain_schema = schema;1255            <Collection<T>>::insert(collection_id, target_collection);12561257            Ok(())1258        }12591260        /// Set variable on-chain data schema.1261        /// 1262        /// # Permissions1263        /// 1264        /// * Collection Owner1265        /// * Collection Admin1266        /// 1267        /// # Arguments1268        /// 1269        /// * collection_id.1270        /// 1271        /// * schema: String representing the variable on-chain data schema.1272        #[weight = T::WeightInfo::set_const_on_chain_schema()]1273        pub fn set_variable_on_chain_schema (1274            origin,1275            collection_id: u64,1276            schema: Vec<u8>1277        ) -> DispatchResult {1278            let sender = ensure_signed(origin)?;1279            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12801281            let mut target_collection = <Collection<T>>::get(collection_id);1282            target_collection.variable_on_chain_schema = schema;1283            <Collection<T>>::insert(collection_id, target_collection);12841285            Ok(())1286        }12871288        // Sudo permissions function1289        #[weight = 0]1290        pub fn set_chain_limits(1291            origin,1292            limits: ChainLimits1293        ) -> DispatchResult {1294            ensure_root(origin)?;1295            <ChainLimit>::put(limits);1296            Ok(())1297        }12981299        /// Enable smart contract self-sponsoring.1300        /// 1301        /// # Permissions1302        /// 1303        /// * Contract Owner1304        /// 1305        /// # Arguments1306        /// 1307        /// * contract address1308        /// * enable flag1309        /// 1310        #[weight = T::WeightInfo::enable_contract_sponsoring()]1311        pub fn enable_contract_sponsoring(1312            origin,1313            contract_address: T::AccountId,1314            enable: bool1315        ) -> DispatchResult {13161317            let sender = ensure_signed(origin)?;13181319            #[cfg(feature = "runtime-benchmarks")]1320            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13211322            let mut is_owner = false;1323            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1324                let owner = <ContractOwner<T>>::get(&contract_address);1325                is_owner = sender == owner;1326            }1327            ensure!(is_owner, Error::<T>::NoPermission);13281329            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1330            Ok(())1331        }13321333        /// Set the rate limit for contract sponsoring to specified number of blocks.1334        /// 1335        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1336        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1337        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1338        /// from contract endowment if there are at least B blocks between such transactions. 1339        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1340        /// 1341        /// # Permissions1342        /// 1343        /// * Contract Owner1344        /// 1345        /// # Arguments1346        /// 1347        /// -`contract_address`: Address of the contract to sponsor1348        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1349        /// 1350        #[weight = 0]1351        pub fn set_contract_sponsoring_rate_limit(1352            origin,1353            contract_address: T::AccountId,1354            rate_limit: T::BlockNumber1355        ) -> DispatchResult {1356            let sender = ensure_signed(origin)?;1357            let mut is_owner = false;1358            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1359                let owner = <ContractOwner<T>>::get(&contract_address);1360                is_owner = sender == owner;1361            }1362            ensure!(is_owner, Error::<T>::NoPermission);13631364            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1365            Ok(())1366        }13671368        // #[cfg(feature = "runtime-benchmarks")]1369        // #[weight = 0]1370        // pub fn add_contract_sponsoring_debug(1371        //     origin,1372        //     contract_address: T::AccountId, 1373        //     owner: T::AccountId) -> DispatchResult {1374        //     let sender = ensure_signed(origin)?;1375        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);1376        //     Ok(())1377        // }1378    1379    }1380}13811382impl<T: Trait> Module<T> {13831384    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13851386        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1387            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1388            Self::check_white_list(collection_id, owner)?;1389            Self::check_white_list(collection_id, sender)?;1390        }13911392        Ok(())1393    }13941395    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1396        match target_collection.mode1397        {1398            CollectionMode::NFT => {1399                if let CreateItemData::NFT(data) = data {1400                    // check sizes1401                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1402                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1403                } else {1404                    fail!("Not NFT item data used to mint in NFT collection.");1405                }1406            },1407            CollectionMode::Fungible(_) => {1408                if let CreateItemData::Fungible(_) = data {1409                } else {1410                    fail!("Not Fungible item data used to mint in Fungible collection.");1411                }1412            },1413            CollectionMode::ReFungible(_) => {1414                if let CreateItemData::ReFungible(data) = data {14151416                    // check sizes1417                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1418                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1419                } else {1420                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1421                }1422            },1423            _ => { fail!("Unexpected collection type."); }1424        };14251426        Ok(())1427    }14281429    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1430        match data1431        {1432            CreateItemData::NFT(data) => {1433                let item = NftItemType {1434                    collection: collection_id,1435                    owner,1436                    const_data: data.const_data,1437                    variable_data: data.variable_data1438                };14391440                Self::add_nft_item(item)?;1441            },1442            CreateItemData::Fungible(_) => {1443                let item = FungibleItemType {1444                    collection: collection_id,1445                    owner,1446                    value: (10 as u128).pow(collection.decimal_points)1447                };14481449                Self::add_fungible_item(item)?;1450            },1451            CreateItemData::ReFungible(data) => {1452                let mut owner_list = Vec::new();1453                let value = (10 as u128).pow(collection.decimal_points);1454                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14551456                let item = ReFungibleItemType {1457                    collection: collection_id,1458                    owner: owner_list,1459                    const_data: data.const_data,1460                    variable_data: data.variable_data1461                };14621463                Self::add_refungible_item(item)?;1464            }1465        };146614671468        // call event1469        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14701471        Ok(())1472    }14731474    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1475        let current_index = <ItemListIndex>::get(item.collection)1476            .checked_add(1)1477            .ok_or(Error::<T>::NumOverflow)?;1478        let itemcopy = item.clone();1479        let owner = item.owner.clone();1480        let value = item.value as u64;14811482        Self::add_token_index(item.collection, current_index, owner.clone())?;14831484        <ItemListIndex>::insert(item.collection, current_index);1485        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14861487        // Add current block1488        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1489        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1490        1491        // Update balance1492        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1493            .checked_add(value)1494            .ok_or(Error::<T>::NumOverflow)?;1495        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14961497        Ok(())1498    }14991500    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1501        let current_index = <ItemListIndex>::get(item.collection)1502            .checked_add(1)1503            .ok_or(Error::<T>::NumOverflow)?;1504        let itemcopy = item.clone();15051506        let value = item.owner.first().unwrap().fraction as u64;1507        let owner = item.owner.first().unwrap().owner.clone();15081509        Self::add_token_index(item.collection, current_index, owner.clone())?;15101511        <ItemListIndex>::insert(item.collection, current_index);1512        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15131514        // Add current block1515        let block_number: T::BlockNumber = 0.into();1516        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15171518        // Update balance1519        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1520            .checked_add(value)1521            .ok_or(Error::<T>::NumOverflow)?;1522        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15231524        Ok(())1525    }15261527    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1528        let current_index = <ItemListIndex>::get(item.collection)1529            .checked_add(1)1530            .ok_or(Error::<T>::NumOverflow)?;15311532        let item_owner = item.owner.clone();1533        let collection_id = item.collection.clone();1534        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15351536        <ItemListIndex>::insert(collection_id, current_index);1537        <NftItemList<T>>::insert(collection_id, current_index, item);15381539        // Add current block1540        let block_number: T::BlockNumber = 0.into();1541        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15421543        // Update balance1544        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1545            .checked_add(1)1546            .ok_or(Error::<T>::NumOverflow)?;1547        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15481549        Ok(())1550    }15511552    fn burn_refungible_item(1553        collection_id: u64,1554        item_id: u64,1555        owner: T::AccountId,1556    ) -> DispatchResult {1557        ensure!(1558            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1559            Error::<T>::TokenNotFound1560        );1561        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1562        let item = collection1563            .owner1564            .iter()1565            .filter(|&i| i.owner == owner)1566            .next()1567            .unwrap();1568        Self::remove_token_index(collection_id, item_id, owner.clone())?;15691570        // remove approve list1571        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15721573        // update balance1574        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1575            .checked_sub(item.fraction as u64)1576            .ok_or(Error::<T>::NumOverflow)?;1577        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15781579        <ReFungibleItemList<T>>::remove(collection_id, item_id);15801581        Ok(())1582    }15831584    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1585        ensure!(1586            <NftItemList<T>>::contains_key(collection_id, item_id),1587            Error::<T>::TokenNotFound1588        );1589        let item = <NftItemList<T>>::get(collection_id, item_id);1590        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15911592        // remove approve list1593        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15941595        // update balance1596        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1597            .checked_sub(1)1598            .ok_or(Error::<T>::NumOverflow)?;1599        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1600        <NftItemList<T>>::remove(collection_id, item_id);16011602        Ok(())1603    }16041605    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1606        ensure!(1607            <FungibleItemList<T>>::contains_key(collection_id, item_id),1608            Error::<T>::TokenNotFound1609        );1610        let item = <FungibleItemList<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(item.value as u64)1619            .ok_or(Error::<T>::NumOverflow)?;1620        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16211622        <FungibleItemList<T>>::remove(collection_id, item_id);16231624        Ok(())1625    }16261627    fn collection_exists(collection_id: u64) -> DispatchResult {1628        ensure!(1629            <Collection<T>>::contains_key(collection_id),1630            Error::<T>::CollectionNotFound1631        );1632        Ok(())1633    }16341635    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1636        Self::collection_exists(collection_id)?;16371638        let target_collection = <Collection<T>>::get(collection_id);1639        ensure!(1640            subject == target_collection.owner,1641            Error::<T>::NoPermission1642        );16431644        Ok(())1645    }16461647    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1648        let target_collection = <Collection<T>>::get(collection_id);1649        let mut result: bool = subject == target_collection.owner;1650        let exists = <AdminList<T>>::contains_key(collection_id);16511652        if !result & exists {1653            if <AdminList<T>>::get(collection_id).contains(&subject) {1654                result = true1655            }1656        }16571658        result1659    }16601661    fn check_owner_or_admin_permissions(1662        collection_id: u64,1663        subject: T::AccountId,1664    ) -> DispatchResult {1665        Self::collection_exists(collection_id)?;1666        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16671668        ensure!(1669            result,1670            Error::<T>::NoPermission1671        );1672        Ok(())1673    }16741675    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1676        let target_collection = <Collection<T>>::get(collection_id);16771678        match target_collection.mode {1679            CollectionMode::NFT => {1680                <NftItemList<T>>::get(collection_id, item_id).owner == subject1681            }1682            CollectionMode::Fungible(_) => {1683                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1684            }1685            CollectionMode::ReFungible(_) => {1686                <ReFungibleItemList<T>>::get(collection_id, item_id)1687                    .owner1688                    .iter()1689                    .any(|i| i.owner == subject)1690            }1691            CollectionMode::Invalid => false,1692        }1693    }16941695    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1696        let mes = Error::<T>::AddresNotInWhiteList;1697        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1698        let wl = <WhiteList<T>>::get(collection_id);1699        ensure!(wl.contains(address), mes);17001701        Ok(())1702    }17031704    fn transfer_fungible(1705        collection_id: u64,1706        item_id: u64,1707        value: u64,1708        owner: T::AccountId,1709        new_owner: T::AccountId,1710    ) -> DispatchResult {1711        ensure!(1712            <FungibleItemList<T>>::contains_key(collection_id, item_id),1713            Error::<T>::TokenNotFound1714        );17151716        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1717        let amount = full_item.value;17181719        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17201721        // update balance1722        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1723            .checked_sub(value)1724            .ok_or(Error::<T>::NumOverflow)?;1725        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17261727        let mut new_owner_account_id = 0;1728        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1729        if new_owner_items.len() > 0 {1730            new_owner_account_id = new_owner_items[0];1731        }17321733        let val64 = value.into();17341735        // transfer1736        if amount == val64 && new_owner_account_id == 0 {1737            // change owner1738            // new owner do not have account1739            let mut new_full_item = full_item.clone();1740            new_full_item.owner = new_owner.clone();1741            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17421743            // update balance1744            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1745                .checked_add(value)1746                .ok_or(Error::<T>::NumOverflow)?;1747            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17481749            // update index collection1750            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1751        } else {1752            let mut new_full_item = full_item.clone();1753            new_full_item.value -= val64;17541755            // separate amount1756            if new_owner_account_id > 0 {1757                // new owner has account1758                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1759                item.value += val64;17601761                // update balance1762                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1763                    .checked_add(value)1764                    .ok_or(Error::<T>::NumOverflow)?;1765                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17661767                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1768            } else {1769                // new owner do not have account1770                let item = FungibleItemType {1771                    collection: collection_id,1772                    owner: new_owner.clone(),1773                    value: val64,1774                };17751776                Self::add_fungible_item(item)?;1777            }17781779            if amount == val64 {1780                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17811782                // remove approve list1783                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1784                <FungibleItemList<T>>::remove(collection_id, item_id);1785            }17861787            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1788        }17891790        Ok(())1791    }17921793    fn transfer_refungible(1794        collection_id: u64,1795        item_id: u64,1796        value: u64,1797        owner: T::AccountId,1798        new_owner: T::AccountId,1799    ) -> DispatchResult {1800        ensure!(1801            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1802            Error::<T>::TokenNotFound1803        );18041805        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1806        let item = full_item1807            .owner1808            .iter()1809            .filter(|i| i.owner == owner)1810            .next()1811            .ok_or(Error::<T>::NumOverflow)?;1812        let amount = item.fraction;18131814        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18151816        // update balance1817        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1818            .checked_sub(value)1819            .ok_or(Error::<T>::NumOverflow)?;1820        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18211822        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1823            .checked_add(value)1824            .ok_or(Error::<T>::NumOverflow)?;1825        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18261827        let old_owner = item.owner.clone();1828        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1829        let val64 = value.into();18301831        // transfer1832        if amount == val64 && !new_owner_has_account {1833            // change owner1834            // new owner do not have account1835            let mut new_full_item = full_item.clone();1836            new_full_item1837                .owner1838                .iter_mut()1839                .find(|i| i.owner == owner)1840                .unwrap()1841                .owner = new_owner.clone();1842            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18431844            // update index collection1845            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1846        } else {1847            let mut new_full_item = full_item.clone();1848            new_full_item1849                .owner1850                .iter_mut()1851                .find(|i| i.owner == owner)1852                .unwrap()1853                .fraction -= val64;18541855            // separate amount1856            if new_owner_has_account {1857                // new owner has account1858                new_full_item1859                    .owner1860                    .iter_mut()1861                    .find(|i| i.owner == new_owner)1862                    .unwrap()1863                    .fraction += val64;1864            } else {1865                // new owner do not have account1866                new_full_item.owner.push(Ownership {1867                    owner: new_owner.clone(),1868                    fraction: val64,1869                });1870                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1871            }18721873            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1874        }18751876        Ok(())1877    }18781879    fn transfer_nft(1880        collection_id: u64,1881        item_id: u64,1882        sender: T::AccountId,1883        new_owner: T::AccountId,1884    ) -> DispatchResult {1885        ensure!(1886            <NftItemList<T>>::contains_key(collection_id, item_id),1887            Error::<T>::TokenNotFound1888        );18891890        let mut item = <NftItemList<T>>::get(collection_id, item_id);18911892        ensure!(1893            sender == item.owner,1894            Error::<T>::MustBeTokenOwner1895        );18961897        // update balance1898        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1899            .checked_sub(1)1900            .ok_or(Error::<T>::NumOverflow)?;1901        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19021903        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1904            .checked_add(1)1905            .ok_or(Error::<T>::NumOverflow)?;1906        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19071908        // change owner1909        let old_owner = item.owner.clone();1910        item.owner = new_owner.clone();1911        <NftItemList<T>>::insert(collection_id, item_id, item);19121913        // update index collection1914        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19151916        // reset approved list1917        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1918        Ok(())1919    }1920    1921    fn item_exists(1922        collection_id: u64,1923        item_id: u64,1924        mode: &CollectionMode1925    ) -> DispatchResult {1926        match mode {1927            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1928            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1929            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1930            _ => ()1931        };1932        1933        Ok(())1934    }19351936    fn set_re_fungible_variable_data(1937        collection_id: u64,1938        item_id: u64,1939        data: Vec<u8>1940    ) -> DispatchResult {1941        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19421943        item.variable_data = data;19441945        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19461947        Ok(())1948    }19491950    fn set_nft_variable_data(1951        collection_id: u64,1952        item_id: u64,1953        data: Vec<u8>1954    ) -> DispatchResult {1955        let mut item = <NftItemList<T>>::get(collection_id, item_id);1956        1957        item.variable_data = data;19581959        <NftItemList<T>>::insert(collection_id, item_id, item);1960        1961        Ok(())1962    }19631964    fn init_collection(item: &CollectionType<T::AccountId>) {1965        // check params1966        assert!(1967            item.decimal_points <= 4,1968            "decimal_points parameter must be lower than 4"1969        );1970        assert!(1971            item.name.len() <= 64,1972            "Collection name can not be longer than 63 char"1973        );1974        assert!(1975            item.name.len() <= 256,1976            "Collection description can not be longer than 255 char"1977        );1978        assert!(1979            item.token_prefix.len() <= 16,1980            "Token prefix can not be longer than 15 char"1981        );19821983        // Generate next collection ID1984        let next_id = CreatedCollectionCount::get()1985            .checked_add(1)1986            .unwrap();19871988        CreatedCollectionCount::put(next_id);1989    }19901991    fn init_nft_token(item: &NftItemType<T::AccountId>) {1992        let current_index = <ItemListIndex>::get(item.collection)1993            .checked_add(1)1994            .unwrap();19951996        let item_owner = item.owner.clone();1997        let collection_id = item.collection.clone();1998        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19992000        <ItemListIndex>::insert(collection_id, current_index);20012002        // Update balance2003        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2004            .checked_add(1)2005            .unwrap();2006        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2007    }20082009    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2010        let current_index = <ItemListIndex>::get(item.collection)2011            .checked_add(1)2012            .unwrap();2013        let owner = item.owner.clone();2014        let value = item.value as u64;20152016        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20172018        <ItemListIndex>::insert(item.collection, current_index);20192020        // Update balance2021        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2022            .checked_add(value)2023            .unwrap();2024        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2025    }20262027    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2028        let current_index = <ItemListIndex>::get(item.collection)2029            .checked_add(1)2030            .unwrap();20312032        let value = item.owner.first().unwrap().fraction as u64;2033        let owner = item.owner.first().unwrap().owner.clone();20342035        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20362037        <ItemListIndex>::insert(item.collection, current_index);20382039        // Update balance2040        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2041            .checked_add(value)2042            .unwrap();2043        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2044    }20452046    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20472048        // add to account limit2049        if <AccountItemCount<T>>::contains_key(owner.clone()) {20502051            // bound Owned tokens by a single address2052            let count = <AccountItemCount<T>>::get(owner.clone());2053            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20542055            <AccountItemCount<T>>::insert(owner.clone(), count2056                .checked_add(1)2057                .ok_or(Error::<T>::NumOverflow)?);2058        }2059        else {2060            <AccountItemCount<T>>::insert(owner.clone(), 1);2061        }20622063        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2064        if list_exists {2065            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2066            let item_contains = list.contains(&item_index.clone());20672068            if !item_contains {2069                list.push(item_index.clone());2070            }20712072            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2073        } else {2074            let mut itm = Vec::new();2075            itm.push(item_index.clone());2076            <AddressTokens<T>>::insert(collection_id, owner, itm);2077            2078        }20792080        Ok(())2081    }20822083    fn remove_token_index(2084        collection_id: u64,2085        item_index: u64,2086        owner: T::AccountId,2087    ) -> DispatchResult {20882089        // update counter2090        <AccountItemCount<T>>::insert(owner.clone(), 2091            <AccountItemCount<T>>::get(owner.clone())2092            .checked_sub(1)2093            .ok_or(Error::<T>::NumOverflow)?);209420952096        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2097        if list_exists {2098            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2099            let item_contains = list.contains(&item_index.clone());21002101            if item_contains {2102                list.retain(|&item| item != item_index);2103                <AddressTokens<T>>::insert(collection_id, owner, list);2104            }2105        }21062107        Ok(())2108    }21092110    fn move_token_index(2111        collection_id: u64,2112        item_index: u64,2113        old_owner: T::AccountId,2114        new_owner: T::AccountId,2115    ) -> DispatchResult {2116        Self::remove_token_index(collection_id, item_index, old_owner)?;2117        Self::add_token_index(collection_id, item_index, new_owner)?;21182119        Ok(())2120    }2121}21222123////////////////////////////////////////////////////////////////////////////////////////////////////2124// Economic models2125// #region21262127/// Fee multiplier.2128pub type Multiplier = FixedU128;21292130type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2131    <T as system::Trait>::AccountId,2132>>::Balance;2133type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2134    <T as system::Trait>::AccountId,2135>>::NegativeImbalance;21362137/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2138/// in the queue.2139#[derive(Encode, Decode, Clone, Eq, PartialEq)]2140pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2141    #[codec(compact)] BalanceOf<T>2142);21432144impl<T: Trait + Send + Sync> sp_std::fmt::Debug2145    for ChargeTransactionPayment<T>2146{2147    #[cfg(feature = "std")]2148    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2149        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2150    }2151    #[cfg(not(feature = "std"))]2152    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2153        Ok(())2154    }2155}21562157impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2158where2159    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2160    BalanceOf<T>: Send + Sync + FixedPointOperand,2161{2162    /// utility constructor. Used only in client/factory code.2163    pub fn from(fee: BalanceOf<T>) -> Self {2164        Self(fee)2165    }21662167    pub fn traditional_fee(2168        len: usize,2169        info: &DispatchInfoOf<T::Call>,2170        tip: BalanceOf<T>,2171    ) -> BalanceOf<T>2172    where2173        T::Call: Dispatchable<Info = DispatchInfo>,2174    {2175        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2176    }21772178    fn withdraw_fee(2179        &self,2180        who: &T::AccountId,2181        call: &T::Call,2182        info: &DispatchInfoOf<T::Call>,2183        len: usize,2184    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2185        let tip = self.0;21862187        // Set fee based on call type. Creating collection costs 1 Unique.2188        // All other transactions have traditional fees so far2189        // let fee = match call.is_sub_type() {2190        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2191        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2192        //                                                 // _ => <BalanceOf<T>>::from(100)2193        // };2194        let fee = Self::traditional_fee(len, info, tip);21952196        // Determine who is paying transaction fee based on ecnomic model2197        // Parse call to extract collection ID and access collection sponsor2198        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2199            Some(Call::create_item(collection_id, _properties, _owner)) => {2200                <Collection<T>>::get(collection_id).sponsor2201            }2202            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2203                let _collection_mode = <Collection<T>>::get(collection_id).mode;22042205                // sponsor timeout2206                let sponsor_transfer = match _collection_mode {2207                    CollectionMode::NFT => {2208                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2209                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2210                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2211                        if block_number >= limit_time {2212                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2213                            true2214                        }2215                        else {2216                            false2217                        }2218                    }2219                    CollectionMode::Fungible(_) => {2220                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2221                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2222                        if basket.iter().any(|i| i.address == _new_owner.clone())2223                        {2224                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2225                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2226                            if block_number >= limit_time {2227                                basket.retain(|x| x.address == item.address);2228                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2229                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2230                                true2231                            }2232                            else {2233                                false2234                            }2235                        }2236                        else {2237                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2238                            true2239                        }2240                    }2241                    CollectionMode::ReFungible(_) => {2242                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2243                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2244                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2245                        if block_number >= limit_time {2246                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2247                            true2248                        } else {2249                            false2250                        }2251                    }2252                    _ => {2253                        false2254                    },2255                };22562257                if !sponsor_transfer {2258                    T::AccountId::default()2259                } else {2260                    <Collection<T>>::get(collection_id).sponsor2261                }2262            }22632264            _ => T::AccountId::default(),2265        };22662267        // Sponsor smart contracts2268        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22692270            // On instantiation: set the contract owner2271            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22722273                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2274                    code_hash,2275                    &data,2276                    &who,2277                );2278                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22792280                T::AccountId::default()2281            },22822283            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2284            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22852286                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22872288                let mut sponsor_transfer = false;2289                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2290                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2291                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2292                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2293                    let limit_time = last_tx_block + rate_limit;22942295                    if block_number >= limit_time {2296                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2297                        sponsor_transfer = true;2298                    }2299                } else {2300                    sponsor_transfer = false;2301                }2302               2303                2304                let mut sp = T::AccountId::default();2305                if sponsor_transfer {2306                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2307                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2308                            sp = called_contract;2309                        }2310                    }2311                }23122313                sp2314            },23152316            _ => sponsor,2317        };23182319        let mut who_pays_fee: T::AccountId = sponsor.clone();2320        if sponsor == T::AccountId::default() {2321            who_pays_fee = who.clone();2322        }23232324        // Only mess with balances if fee is not zero.2325        if fee.is_zero() {2326            return Ok((fee, None));2327        }23282329        match <T as transaction_payment::Trait>::Currency::withdraw(2330            &who_pays_fee,2331            fee,2332            if tip.is_zero() {2333                WithdrawReason::TransactionPayment.into()2334            } else {2335                WithdrawReason::TransactionPayment | WithdrawReason::Tip2336            },2337            ExistenceRequirement::KeepAlive,2338        ) {2339            Ok(imbalance) => Ok((fee, Some(imbalance))),2340            Err(_) => Err(InvalidTransaction::Payment.into()),2341        }2342    }2343}234423452346impl<T: Trait + Send + Sync> SignedExtension2347    for ChargeTransactionPayment<T>2348where2349    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2350    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2351{2352    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2353    type AccountId = T::AccountId;2354    type Call = T::Call;2355    type AdditionalSigned = ();2356    type Pre = (2357        BalanceOf<T>,2358        Self::AccountId,2359        Option<NegativeImbalanceOf<T>>,2360        BalanceOf<T>,2361    );2362    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2363        Ok(())2364    }23652366    fn validate(2367        &self,2368        _who: &Self::AccountId,2369        _call: &Self::Call,2370        _info: &DispatchInfoOf<Self::Call>,2371        _len: usize,2372    ) -> TransactionValidity {2373        Ok(ValidTransaction::default())2374    }23752376    fn pre_dispatch(2377        self,2378        who: &Self::AccountId,2379        call: &Self::Call,2380        info: &DispatchInfoOf<Self::Call>,2381        len: usize,2382    ) -> Result<Self::Pre, TransactionValidityError> {2383        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2384        Ok((self.0, who.clone(), imbalance, fee))2385    }23862387    fn post_dispatch(2388        pre: Self::Pre,2389        info: &DispatchInfoOf<Self::Call>,2390        post_info: &PostDispatchInfoOf<Self::Call>,2391        len: usize,2392        _result: &DispatchResult,2393    ) -> Result<(), TransactionValidityError> {2394        let (tip, who, imbalance, fee) = pre;2395        if let Some(payed) = imbalance {2396            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2397                len as u32, info, post_info, tip,2398            );2399            let refund = fee.saturating_sub(actual_fee);2400            let actual_payment =2401                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2402                    &who, refund,2403                ) {2404                    Ok(refund_imbalance) => {2405                        // The refund cannot be larger than the up front payed max weight.2406                        // `PostDispatchInfo::calc_unspent` guards against such a case.2407                        match payed.offset(refund_imbalance) {2408                            Ok(actual_payment) => actual_payment,2409                            Err(_) => return Err(InvalidTransaction::Payment.into()),2410                        }2411                    }2412                    // We do not recreate the account using the refund. The up front payment2413                    // is gone in that case.2414                    Err(_) => payed,2415                };2416            let imbalances = actual_payment.split(tip);2417            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2418                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2419            );2420        }2421        Ok(())2422    }2423}24242425// #endregion