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

difftreelog

source

pallets/nft/src/lib.rs91.7 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;4748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4950// Structs51// #region5253pub type CollectionId = u32;54pub type TokenId = u32;5556pub type DecimalPoints = u8;5758#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]59#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]60pub enum CollectionMode {61    Invalid,62    NFT,63    // decimal points64    Fungible(DecimalPoints),65    // decimal points66    ReFungible(DecimalPoints),67}6869impl Into<u8> for CollectionMode {70    fn into(self) -> u8 {71        match self {72            CollectionMode::Invalid => 0,73            CollectionMode::NFT => 1,74            CollectionMode::Fungible(_) => 2,75            CollectionMode::ReFungible(_) => 3,76        }77    }78}7980#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]81#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]82pub enum AccessMode {83    Normal,84    WhiteList,85}86impl Default for AccessMode {87    fn default() -> Self {88        Self::Normal89    }90}9192impl Default for CollectionMode {93    fn default() -> Self {94        Self::Invalid95    }96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct Ownership<AccountId> {101    pub owner: AccountId,102    pub fraction: u128,103}104105#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]106#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]107pub struct CollectionType<AccountId> {108    pub owner: AccountId,109    pub mode: CollectionMode,110    pub access: AccessMode,111    pub decimal_points: DecimalPoints,112    pub name: Vec<u16>,        // 64 include null escape char113    pub description: Vec<u16>, // 256 include null escape char114    pub token_prefix: Vec<u8>, // 16 include null escape char115    pub mint_mode: bool,116    pub offchain_schema: Vec<u8>,117    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender118    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship119    pub limits: CollectionLimits, // Collection private restrictions 120    pub variable_on_chain_schema: Vec<u8>, //121    pub const_on_chain_schema: Vec<u8>, //122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: CollectionId,128    pub owner: AccountId,129    pub const_data: Vec<u8>,130    pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136    pub collection: CollectionId,137    pub owner: AccountId,138    pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144    pub collection: CollectionId,145    pub owner: Vec<Ownership<AccountId>>,146    pub const_data: Vec<u8>,147    pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153    pub approved: AccountId,154    pub amount: u128,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160    pub sender: AccountId,161    pub recipient: AccountId,162    pub collection_id: CollectionId,163    pub item_id: TokenId,164    pub amount: u64,165    pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171    pub address: AccountId,172    pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct CollectionLimits {178    pub account_token_ownership_limit: u32,179    pub sponsored_data_size: u32,180    pub token_limit: u32,181182    // Timeouts for item types in passed blocks183    pub sponsor_transfer_timeout: u32,184}185186impl Default for CollectionLimits {187    fn default() -> CollectionLimits {188        CollectionLimits { 189            account_token_ownership_limit: u32::max_value(), 190            token_limit: u32::max_value(),191            sponsored_data_size: u32::max_value(), 192            sponsor_transfer_timeout: u32::max_value() }193    }194}195196#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct ChainLimits {199    pub collection_numbers_limit: u32,200    pub account_token_ownership_limit: u32,201    pub collections_admins_limit: u64,202    pub custom_data_limit: u32,203204    // Timeouts for item types in passed blocks205    pub nft_sponsor_transfer_timeout: u32,206    pub fungible_sponsor_transfer_timeout: u32,207    pub refungible_sponsor_transfer_timeout: u32,208}209210pub trait WeightInfo {211	fn create_collection() -> Weight;212	fn destroy_collection() -> Weight;213	fn add_to_white_list() -> Weight;214	fn remove_from_white_list() -> Weight;215    fn set_public_access_mode() -> Weight;216    fn set_mint_permission() -> Weight;217    fn change_collection_owner() -> Weight;218    fn add_collection_admin() -> Weight;219    fn remove_collection_admin() -> Weight;220    fn set_collection_sponsor() -> Weight;221    fn confirm_sponsorship() -> Weight;222    fn remove_collection_sponsor() -> Weight;223    fn create_item(s: usize) -> Weight;224    fn burn_item() -> Weight;225    fn transfer() -> Weight;226    fn approve() -> Weight;227    fn transfer_from() -> Weight;228    fn set_offchain_schema() -> Weight;229    fn set_const_on_chain_schema() -> Weight;230    fn set_variable_on_chain_schema() -> Weight;231    fn set_variable_meta_data() -> Weight;232    fn enable_contract_sponsoring() -> Weight;233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct CreateNftData {238    pub const_data: Vec<u8>,239    pub variable_data: Vec<u8>,240}241242#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]243#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]244pub struct CreateFungibleData {245}246247#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]248#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]249pub struct CreateReFungibleData {250    pub const_data: Vec<u8>,251    pub variable_data: Vec<u8>,252}253254#[derive(Encode, Decode, Debug, Clone, PartialEq)]255#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]256pub enum CreateItemData {257    NFT(CreateNftData),258    Fungible(CreateFungibleData),259    ReFungible(CreateReFungibleData)260}261262impl CreateItemData {263    pub fn len(&self) -> usize {264        let len = match self {265            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),266            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),267            _ => 0268        };269        270        return len;271    }272}273274impl From<CreateNftData> for CreateItemData {275    fn from(item: CreateNftData) -> Self {276        CreateItemData::NFT(item)277    }278}279280impl From<CreateReFungibleData> for CreateItemData {281    fn from(item: CreateReFungibleData) -> Self {282        CreateItemData::ReFungible(item)283    }284}285286impl From<CreateFungibleData> for CreateItemData {287    fn from(item: CreateFungibleData) -> Self {288        CreateItemData::Fungible(item)289    }290}291292293decl_error! {294	/// Error for non-fungible-token module.295	pub enum Error for Module<T: Trait> {296        /// Total collections bound exceeded.297        TotalCollectionsLimitExceeded,298		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.299        CollectionDecimalPointLimitExceeded, 300        /// Collection name can not be longer than 63 char.301        CollectionNameLimitExceeded, 302        /// Collection description can not be longer than 255 char.303        CollectionDescriptionLimitExceeded, 304        /// Token prefix can not be longer than 15 char.305        CollectionTokenPrefixLimitExceeded,306        /// This collection does not exist.307        CollectionNotFound,308        /// Item not exists.309        TokenNotFound,310        /// Arithmetic calculation overflow.311        NumOverflow,       312        /// Account already has admin role.313        AlreadyAdmin,  314        /// You do not own this collection.315        NoPermission,316        /// This address is not set as sponsor, use setCollectionSponsor first.317        ConfirmUnsetSponsorFail,318        /// Collection is not in mint mode.319        PublicMintingNotAllowed,320        /// Sender parameter and item owner must be equal.321        MustBeTokenOwner,322        /// Item balance not enough.323        TokenValueTooLow,324        /// Size of item is too large.325        NftSizeLimitExceeded,326        /// No approve found327        ApproveNotFound,328        /// Requested value more than approved.329        TokenValueNotEnough,330        /// Only approved addresses can call this method.331        ApproveRequired,332        /// Address is not in white list.333        AddresNotInWhiteList,334        /// Number of collection admins bound exceeded.335        CollectionAdminsLimitExceeded,336        /// Owned tokens by a single address bound exceeded.337        AddressOwnershipLimitExceeded,338        /// Length of items properties must be greater than 0.339        EmptyArgument,340        /// const_data exceeded data limit.341        TokenConstDataLimitExceeded,342        /// variable_data exceeded data limit.343        TokenVariableDataLimitExceeded,344        /// Not NFT item data used to mint in NFT collection.345        NotNftDataUsedToMintNftCollectionToken,346        /// Not Fungible item data used to mint in Fungible collection.347        NotFungibleDataUsedToMintFungibleCollectionToken,348        /// Not Re Fungible item data used to mint in Re Fungible collection.349        NotReFungibleDataUsedToMintReFungibleCollectionToken,350        /// Unexpected collection type.351        UnexpectedCollectionType,352        /// Can't store metadata in fungible tokens.353        CantStoreMetadataInFungibleTokens,354        /// Collection token limit exceeded355        CollectionTokenLimitExceeded,356        /// Account token limit exceeded per collection357        AccountTokenLimitExceeded358	}359}360361pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {362    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;363364    /// Weight information for extrinsics in this pallet.365	type WeightInfo: WeightInfo;366}367368#[cfg(feature = "runtime-benchmarks")]369mod benchmarking;370371// #endregion372373decl_storage! {374    trait Store for Module<T: Trait> as Nft {375376        // Private members377        NextCollectionID: CollectionId;378        CreatedCollectionCount: u32;379        ChainVersion: u64;380        ItemListIndex: map hasher(identity) CollectionId => TokenId;381382        // Chain limits struct383        pub ChainLimit get(fn chain_limit) config(): ChainLimits;384385        // Bound counters386        CollectionCount: u32;387        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;388389        // Basic collections390        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;391        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;392        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;393394        /// Balance owner per collection map395        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;396397        /// second parameter: item id + owner account id398        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;399400        /// Item collections401        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;402        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;403        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;404405        /// Index list406        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;407408        /// Tokens transfer baskets409        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;410        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;411        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;412413        // Contract Sponsorship and Ownership414        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;415        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;416        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;417        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;418    }419    add_extra_genesis {420        build(|config: &GenesisConfig<T>| {421            // Modification of storage422            for (_num, _c) in &config.collection {423                <Module<T>>::init_collection(_c);424            }425426            for (_num, _q, _i) in &config.nft_item_id {427                <Module<T>>::init_nft_token(_i);428            }429430            for (_num, _q, _i) in &config.fungible_item_id {431                <Module<T>>::init_fungible_token(_i);432            }433434            for (_num, _q, _i) in &config.refungible_item_id {435                <Module<T>>::init_refungible_token(_i);436            }437        })438    }439}440441decl_event!(442    pub enum Event<T>443    where444        AccountId = <T as system::Trait>::AccountId,445    {446        /// New collection was created447        /// 448        /// # Arguments449        /// 450        /// * collection_id: Globally unique identifier of newly created collection.451        /// 452        /// * mode: [CollectionMode] converted into u8.453        /// 454        /// * account_id: Collection owner.455        Created(CollectionId, u8, AccountId),456457        /// New item was created.458        /// 459        /// # Arguments460        /// 461        /// * collection_id: Id of the collection where item was created.462        /// 463        /// * item_id: Id of an item. Unique within the collection.464        ItemCreated(CollectionId, TokenId),465466        /// Collection item was burned.467        /// 468        /// # Arguments469        /// 470        /// collection_id.471        /// 472        /// item_id: Identifier of burned NFT.473        ItemDestroyed(CollectionId, TokenId),474    }475);476477decl_module! {478    pub struct Module<T: Trait> for enum Call where origin: T::Origin {479480        fn deposit_event() = default;481        type Error = Error<T>;482483        fn on_initialize(now: T::BlockNumber) -> Weight {484485            if ChainVersion::get() < 2486            {487                let value = NextCollectionID::get();488                CreatedCollectionCount::put(value);489                ChainVersion::put(2);490            }491492            0493        }494495        /// 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.496        /// 497        /// # Permissions498        /// 499        /// * Anyone.500        /// 501        /// # Arguments502        /// 503        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.504        /// 505        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.506        /// 507        /// * token_prefix: UTF-8 string with token prefix.508        /// 509        /// * mode: [CollectionMode] collection type and type dependent data.510        // returns collection ID511        #[weight = T::WeightInfo::create_collection()]512        pub fn create_collection(origin,513                                 collection_name: Vec<u16>,514                                 collection_description: Vec<u16>,515                                 token_prefix: Vec<u8>,516                                 mode: CollectionMode) -> DispatchResult {517518            // Anyone can create a collection519            let who = ensure_signed(origin)?;520521            let decimal_points = match mode {522                CollectionMode::Fungible(points) => points,523                CollectionMode::ReFungible(points) => points,524                _ => 0525            };526527            // bound Total number of collections528            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);529530            // check params531            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);532533            let mut name = collection_name.to_vec();534            name.push(0);535            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);536537            let mut description = collection_description.to_vec();538            description.push(0);539            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);540541            let mut prefix = token_prefix.to_vec();542            prefix.push(0);543            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);544545            // Generate next collection ID546            let next_id = CreatedCollectionCount::get()547                .checked_add(1)548                .ok_or(Error::<T>::NumOverflow)?;549550            // bound counter551            let total = CollectionCount::get()552                .checked_add(1)553                .ok_or(Error::<T>::NumOverflow)?;554555            CreatedCollectionCount::put(next_id);556            CollectionCount::put(total);557558            // Create new collection559            let new_collection = CollectionType {560                owner: who.clone(),561                name: name,562                mode: mode.clone(),563                mint_mode: false,564                access: AccessMode::Normal,565                description: description,566                decimal_points: decimal_points,567                token_prefix: prefix,568                offchain_schema: Vec::new(),569                sponsor: T::AccountId::default(),570                unconfirmed_sponsor: T::AccountId::default(),571                variable_on_chain_schema: Vec::new(),572                const_on_chain_schema: Vec::new(),573                limits: CollectionLimits::default(),574            };575576            // Add new collection to map577            <Collection<T>>::insert(next_id, new_collection);578579            // call event580            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));581582            Ok(())583        }584585        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.586        /// 587        /// # Permissions588        /// 589        /// * Collection Owner.590        /// 591        /// # Arguments592        /// 593        /// * collection_id: collection to destroy.594        #[weight = T::WeightInfo::destroy_collection()]595        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {596597            let sender = ensure_signed(origin)?;598            Self::check_owner_permissions(collection_id, sender)?;599600            <AddressTokens<T>>::remove_prefix(collection_id);601            <ApprovedList<T>>::remove_prefix(collection_id);602            <Balance<T>>::remove_prefix(collection_id);603            <ItemListIndex>::remove(collection_id);604            <AdminList<T>>::remove(collection_id);605            <Collection<T>>::remove(collection_id);606            <WhiteList<T>>::remove(collection_id);607608            <NftItemList<T>>::remove_prefix(collection_id);609            <FungibleItemList<T>>::remove_prefix(collection_id);610            <ReFungibleItemList<T>>::remove_prefix(collection_id);611612            <NftTransferBasket<T>>::remove_prefix(collection_id);613            <FungibleTransferBasket<T>>::remove_prefix(collection_id);614            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);615616            if CollectionCount::get() > 0617            {618                // bound couter619                let total = CollectionCount::get()620                    .checked_sub(1)621                    .ok_or(Error::<T>::NumOverflow)?;622623                CollectionCount::put(total);624            }625626            Ok(())627        }628629        /// Add an address to white list.630        /// 631        /// # Permissions632        /// 633        /// * Collection Owner634        /// * Collection Admin635        /// 636        /// # Arguments637        /// 638        /// * collection_id.639        /// 640        /// * address.641        #[weight = T::WeightInfo::add_to_white_list()]642        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{643644            let sender = ensure_signed(origin)?;645            Self::check_owner_or_admin_permissions(collection_id, sender)?;646647            let mut white_list_collection: Vec<T::AccountId>;648            if <WhiteList<T>>::contains_key(collection_id) {649                white_list_collection = <WhiteList<T>>::get(collection_id);650                if !white_list_collection.contains(&address.clone())651                {652                    white_list_collection.push(address.clone());653                }654            }655            else {656                white_list_collection = Vec::new();657                white_list_collection.push(address.clone());658            }659660            <WhiteList<T>>::insert(collection_id, white_list_collection);661            Ok(())662        }663664        /// Remove an address from white list.665        /// 666        /// # Permissions667        /// 668        /// * Collection Owner669        /// * Collection Admin670        /// 671        /// # Arguments672        /// 673        /// * collection_id.674        /// 675        /// * address.676        #[weight = T::WeightInfo::remove_from_white_list()]677        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{678679            let sender = ensure_signed(origin)?;680            Self::check_owner_or_admin_permissions(collection_id, sender)?;681682            if <WhiteList<T>>::contains_key(collection_id) {683                let mut white_list_collection = <WhiteList<T>>::get(collection_id);684                if white_list_collection.contains(&address.clone())685                {686                    white_list_collection.retain(|i| *i != address.clone());687                    <WhiteList<T>>::insert(collection_id, white_list_collection);688                }689            }690691            Ok(())692        }693694        /// Toggle between normal and white list access for the methods with access for `Anyone`.695        /// 696        /// # Permissions697        /// 698        /// * Collection Owner.699        /// 700        /// # Arguments701        /// 702        /// * collection_id.703        /// 704        /// * mode: [AccessMode]705        #[weight = T::WeightInfo::set_public_access_mode()]706        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult707        {708            let sender = ensure_signed(origin)?;709710            Self::check_owner_permissions(collection_id, sender)?;711            let mut target_collection = <Collection<T>>::get(collection_id);712            target_collection.access = mode;713            <Collection<T>>::insert(collection_id, target_collection);714715            Ok(())716        }717718        /// Allows Anyone to create tokens if:719        /// * White List is enabled, and720        /// * Address is added to white list, and721        /// * This method was called with True parameter722        /// 723        /// # Permissions724        /// * Collection Owner725        ///726        /// # Arguments727        /// 728        /// * collection_id.729        /// 730        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.731        #[weight = T::WeightInfo::set_mint_permission()]732        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult733        {734            let sender = ensure_signed(origin)?;735736            Self::check_owner_permissions(collection_id, sender)?;737            let mut target_collection = <Collection<T>>::get(collection_id);738            target_collection.mint_mode = mint_permission;739            <Collection<T>>::insert(collection_id, target_collection);740741            Ok(())742        }743744        /// Change the owner of the collection.745        /// 746        /// # Permissions747        /// 748        /// * Collection Owner.749        /// 750        /// # Arguments751        /// 752        /// * collection_id.753        /// 754        /// * new_owner.755        #[weight = T::WeightInfo::change_collection_owner()]756        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {757758            let sender = ensure_signed(origin)?;759            Self::check_owner_permissions(collection_id, sender)?;760            let mut target_collection = <Collection<T>>::get(collection_id);761            target_collection.owner = new_owner;762            <Collection<T>>::insert(collection_id, target_collection);763764            Ok(())765        }766767        /// Adds an admin of the Collection.768        /// 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. 769        /// 770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// * Collection Admin.774        /// 775        /// # Arguments776        /// 777        /// * collection_id: ID of the Collection to add admin for.778        /// 779        /// * new_admin_id: Address of new admin to add.780        #[weight = T::WeightInfo::add_collection_admin()]781        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {782783            let sender = ensure_signed(origin)?;784            Self::check_owner_or_admin_permissions(collection_id, sender)?;785            let mut admin_arr: Vec<T::AccountId> = Vec::new();786787            if <AdminList<T>>::contains_key(collection_id)788            {789                admin_arr = <AdminList<T>>::get(collection_id);790                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);791            }792793            // Number of collection admins794            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);795796            admin_arr.push(new_admin_id);797            <AdminList<T>>::insert(collection_id, admin_arr);798799            Ok(())800        }801802        /// 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.803        ///804        /// # Permissions805        /// 806        /// * Collection Owner.807        /// * Collection Admin.808        /// 809        /// # Arguments810        /// 811        /// * collection_id: ID of the Collection to remove admin for.812        /// 813        /// * account_id: Address of admin to remove.814        #[weight = T::WeightInfo::remove_collection_admin()]815        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {816817            let sender = ensure_signed(origin)?;818            Self::check_owner_or_admin_permissions(collection_id, sender)?;819820            if <AdminList<T>>::contains_key(collection_id)821            {822                let mut admin_arr = <AdminList<T>>::get(collection_id);823                admin_arr.retain(|i| *i != account_id);824                <AdminList<T>>::insert(collection_id, admin_arr);825            }826827            Ok(())828        }829830        /// # Permissions831        /// 832        /// * Collection Owner833        /// 834        /// # Arguments835        /// 836        /// * collection_id.837        /// 838        /// * new_sponsor.839        #[weight = T::WeightInfo::set_collection_sponsor()]840        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {841842            let sender = ensure_signed(origin)?;843            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);844845            let mut target_collection = <Collection<T>>::get(collection_id);846            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);847848            target_collection.unconfirmed_sponsor = new_sponsor;849            <Collection<T>>::insert(collection_id, target_collection);850851            Ok(())852        }853854        /// # Permissions855        /// 856        /// * Sponsor.857        /// 858        /// # Arguments859        /// 860        /// * collection_id.861        #[weight = T::WeightInfo::confirm_sponsorship()]862        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {863864            let sender = ensure_signed(origin)?;865            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);866867            let mut target_collection = <Collection<T>>::get(collection_id);868            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);869870            target_collection.sponsor = target_collection.unconfirmed_sponsor;871            target_collection.unconfirmed_sponsor = T::AccountId::default();872            <Collection<T>>::insert(collection_id, target_collection);873874            Ok(())875        }876877        /// Switch back to pay-per-own-transaction model.878        ///879        /// # Permissions880        ///881        /// * Collection owner.882        /// 883        /// # Arguments884        /// 885        /// * collection_id.886        #[weight = T::WeightInfo::remove_collection_sponsor()]887        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {888889            let sender = ensure_signed(origin)?;890            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);891892            let mut target_collection = <Collection<T>>::get(collection_id);893            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);894895            target_collection.sponsor = T::AccountId::default();896            <Collection<T>>::insert(collection_id, target_collection);897898            Ok(())899        }900901        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.902        /// 903        /// # Permissions904        /// 905        /// * Collection Owner.906        /// * Collection Admin.907        /// * Anyone if908        ///     * White List is enabled, and909        ///     * Address is added to white list, and910        ///     * MintPermission is enabled (see SetMintPermission method)911        /// 912        /// # Arguments913        /// 914        /// * collection_id: ID of the collection.915        /// 916        /// * owner: Address, initial owner of the NFT.917        ///918        /// * data: Token data to store on chain.919        // #[weight =920        // (130_000_000 as Weight)921        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))922        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))923        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]924925        #[weight = T::WeightInfo::create_item(data.len())]926        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {927928            let sender = ensure_signed(origin)?;929930            Self::collection_exists(collection_id)?;931932            let target_collection = <Collection<T>>::get(collection_id);933934            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;935            Self::validate_create_item_args(&target_collection, &data)?;936            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;937938            Ok(())939        }940941        /// This method creates multiple instances of NFT Collection created with CreateCollection method.942        /// 943        /// # Permissions944        /// 945        /// * Collection Owner.946        /// * Collection Admin.947        /// * Anyone if948        ///     * White List is enabled, and949        ///     * Address is added to white list, and950        ///     * MintPermission is enabled (see SetMintPermission method)951        /// 952        /// # Arguments953        /// 954        /// * collection_id: ID of the collection.955        /// 956        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].957        /// 958        /// * owner: Address, initial owner of the NFT.959        #[weight = T::WeightInfo::create_item(items_data.into_iter()960                               .map(|data| { data.len() })961                               .sum())]962        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {963964            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);965            let sender = ensure_signed(origin)?;966967            Self::collection_exists(collection_id)?;968            let target_collection = <Collection<T>>::get(collection_id);969970            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;971972            for data in &items_data {973                Self::validate_create_item_args(&target_collection, data)?;974            }975            for data in &items_data {976                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;977            }978979            Ok(())980        }981982        /// Destroys a concrete instance of NFT.983        /// 984        /// # Permissions985        /// 986        /// * Collection Owner.987        /// * Collection Admin.988        /// * Current NFT Owner.989        /// 990        /// # Arguments991        /// 992        /// * collection_id: ID of the collection.993        /// 994        /// * item_id: ID of NFT to burn.995        #[weight = T::WeightInfo::burn_item()]996        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {997998            let sender = ensure_signed(origin)?;999            Self::collection_exists(collection_id)?;10001001            // Transfer permissions check1002            let target_collection = <Collection<T>>::get(collection_id);1003            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1004                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1005                Error::<T>::NoPermission);10061007            if target_collection.access == AccessMode::WhiteList {1008                Self::check_white_list(collection_id, &sender)?;1009            }10101011            match target_collection.mode1012            {1013                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1014                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1015                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1016                _ => ()1017            };10181019            // call event1020            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10211022            Ok(())1023        }10241025        /// Change ownership of the token.1026        /// 1027        /// # Permissions1028        /// 1029        /// * Collection Owner1030        /// * Collection Admin1031        /// * Current NFT owner1032        ///1033        /// # Arguments1034        /// 1035        /// * recipient: Address of token recipient.1036        /// 1037        /// * collection_id.1038        /// 1039        /// * item_id: ID of the item1040        ///     * Non-Fungible Mode: Required.1041        ///     * Fungible Mode: Ignored.1042        ///     * Re-Fungible Mode: Required.1043        /// 1044        /// * value: Amount to transfer.1045        ///     * Non-Fungible Mode: Ignored1046        ///     * Fungible Mode: Must specify transferred amount1047        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1048        #[weight = T::WeightInfo::transfer()]1049        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10501051            let sender = ensure_signed(origin)?;10521053            // Transfer permissions check1054            let target_collection = <Collection<T>>::get(collection_id);1055            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1056                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1057                Error::<T>::NoPermission);10581059            if target_collection.access == AccessMode::WhiteList {1060                Self::check_white_list(collection_id, &sender)?;1061                Self::check_white_list(collection_id, &recipient)?;1062            }10631064            match target_collection.mode1065            {1066                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1067                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1068                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1069                _ => ()1070            };10711072            Ok(())1073        }10741075        /// Set, change, or remove approved address to transfer the ownership of the NFT.1076        /// 1077        /// # Permissions1078        /// 1079        /// * Collection Owner1080        /// * Collection Admin1081        /// * Current NFT owner1082        /// 1083        /// # Arguments1084        /// 1085        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1086        /// 1087        /// * collection_id.1088        /// 1089        /// * item_id: ID of the item.1090        #[weight = T::WeightInfo::approve()]1091        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10921093            let sender = ensure_signed(origin)?;10941095            // Transfer permissions check1096            let target_collection = <Collection<T>>::get(collection_id);1097            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1098                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1099                Error::<T>::NoPermission);11001101            if target_collection.access == AccessMode::WhiteList {1102                Self::check_white_list(collection_id, &sender)?;1103                Self::check_white_list(collection_id, &approved)?;1104            }11051106            // amount param stub1107            let amount = 100000000;11081109            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1110            if list_exists {11111112                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1113                let item_contains = list.iter().any(|i| i.approved == approved);11141115                if !item_contains {1116                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1117                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1118                }1119            } else {11201121                let mut list = Vec::new();1122                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1123                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1124            }11251126            Ok(())1127        }1128        1129        /// 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.1130        /// 1131        /// # Permissions1132        /// * Collection Owner1133        /// * Collection Admin1134        /// * Current NFT owner1135        /// * Address approved by current NFT owner1136        /// 1137        /// # Arguments1138        /// 1139        /// * from: Address that owns token.1140        /// 1141        /// * recipient: Address of token recipient.1142        /// 1143        /// * collection_id.1144        /// 1145        /// * item_id: ID of the item.1146        /// 1147        /// * value: Amount to transfer.1148        #[weight = T::WeightInfo::transfer_from()]1149        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11501151            let sender = ensure_signed(origin)?;1152            let mut appoved_transfer = false;11531154            // Check approve1155            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1156                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1157                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1158                if opt_item.is_some()1159                {1160                    appoved_transfer = true;1161                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1162                }1163            }11641165            // Transfer permissions check1166            let target_collection = <Collection<T>>::get(collection_id);1167                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1168                Error::<T>::NoPermission);11691170            if target_collection.access == AccessMode::WhiteList {1171                Self::check_white_list(collection_id, &sender)?;1172                Self::check_white_list(collection_id, &recipient)?;1173            }11741175            // remove approve1176            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1177                .into_iter().filter(|i| i.approved != sender.clone()).collect();1178            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);117911801181            match target_collection.mode1182            {1183                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1184                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1185                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1186                _ => ()1187            };11881189            Ok(())1190        }11911192        ///1193        #[weight = 0]1194        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11951196            // let no_perm_mes = "You do not have permissions to modify this collection";1197            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1198            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1199            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12001201            // // on_nft_received  call12021203            // Self::transfer(origin, collection_id, item_id, new_owner)?;12041205            Ok(())1206        }12071208        /// Set off-chain data schema.1209        /// 1210        /// # Permissions1211        /// 1212        /// * Collection Owner1213        /// * Collection Admin1214        /// 1215        /// # Arguments1216        /// 1217        /// * collection_id.1218        /// 1219        /// * schema: String representing the offchain data schema.1220        #[weight = T::WeightInfo::set_variable_meta_data()]1221        pub fn set_variable_meta_data (1222            origin,1223            collection_id: CollectionId,1224            item_id: TokenId,1225            data: Vec<u8>1226        ) -> DispatchResult {1227            let sender = ensure_signed(origin)?;1228            1229            Self::collection_exists(collection_id)?;1230            1231            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12321233            // Modify permissions check1234            let target_collection = <Collection<T>>::get(collection_id);1235            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1236                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1237                Error::<T>::NoPermission);12381239            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12401241            match target_collection.mode1242            {1243                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1244                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1245                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1246                _ => fail!(Error::<T>::UnexpectedCollectionType)1247            };12481249            Ok(())1250        }1251        12521253        /// Set off-chain data schema.1254        /// 1255        /// # Permissions1256        /// 1257        /// * Collection Owner1258        /// * Collection Admin1259        /// 1260        /// # Arguments1261        /// 1262        /// * collection_id.1263        /// 1264        /// * schema: String representing the offchain data schema.1265        #[weight = T::WeightInfo::set_offchain_schema()]1266        pub fn set_offchain_schema(1267            origin,1268            collection_id: CollectionId,1269            schema: Vec<u8>1270        ) -> DispatchResult {1271            let sender = ensure_signed(origin)?;1272            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12731274            let mut target_collection = <Collection<T>>::get(collection_id);1275            target_collection.offchain_schema = schema;1276            <Collection<T>>::insert(collection_id, target_collection);12771278            Ok(())1279        }12801281        /// Set const on-chain data schema.1282        /// 1283        /// # Permissions1284        /// 1285        /// * Collection Owner1286        /// * Collection Admin1287        /// 1288        /// # Arguments1289        /// 1290        /// * collection_id.1291        /// 1292        /// * schema: String representing the const on-chain data schema.1293        #[weight = T::WeightInfo::set_const_on_chain_schema()]1294        pub fn set_const_on_chain_schema (1295            origin,1296            collection_id: CollectionId,1297            schema: Vec<u8>1298        ) -> DispatchResult {1299            let sender = ensure_signed(origin)?;1300            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13011302            let mut target_collection = <Collection<T>>::get(collection_id);1303            target_collection.const_on_chain_schema = schema;1304            <Collection<T>>::insert(collection_id, target_collection);13051306            Ok(())1307        }13081309        /// Set variable on-chain data schema.1310        /// 1311        /// # Permissions1312        /// 1313        /// * Collection Owner1314        /// * Collection Admin1315        /// 1316        /// # Arguments1317        /// 1318        /// * collection_id.1319        /// 1320        /// * schema: String representing the variable on-chain data schema.1321        #[weight = T::WeightInfo::set_const_on_chain_schema()]1322        pub fn set_variable_on_chain_schema (1323            origin,1324            collection_id: CollectionId,1325            schema: Vec<u8>1326        ) -> DispatchResult {1327            let sender = ensure_signed(origin)?;1328            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13291330            let mut target_collection = <Collection<T>>::get(collection_id);1331            target_collection.variable_on_chain_schema = schema;1332            <Collection<T>>::insert(collection_id, target_collection);13331334            Ok(())1335        }13361337        // Sudo permissions function1338        #[weight = 0]1339        pub fn set_chain_limits(1340            origin,1341            limits: ChainLimits1342        ) -> DispatchResult {1343            ensure_root(origin)?;1344            <ChainLimit>::put(limits);1345            Ok(())1346        }13471348        /// Enable smart contract self-sponsoring.1349        /// 1350        /// # Permissions1351        /// 1352        /// * Contract Owner1353        /// 1354        /// # Arguments1355        /// 1356        /// * contract address1357        /// * enable flag1358        /// 1359        #[weight = T::WeightInfo::enable_contract_sponsoring()]1360        pub fn enable_contract_sponsoring(1361            origin,1362            contract_address: T::AccountId,1363            enable: bool1364        ) -> DispatchResult {13651366            let sender = ensure_signed(origin)?;13671368            #[cfg(feature = "runtime-benchmarks")]1369            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13701371            let mut is_owner = false;1372            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1373                let owner = <ContractOwner<T>>::get(&contract_address);1374                is_owner = sender == owner;1375            }1376            ensure!(is_owner, Error::<T>::NoPermission);13771378            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1379            Ok(())1380        }13811382        /// Set the rate limit for contract sponsoring to specified number of blocks.1383        /// 1384        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1385        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1386        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1387        /// from contract endowment if there are at least B blocks between such transactions. 1388        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1389        /// 1390        /// # Permissions1391        /// 1392        /// * Contract Owner1393        /// 1394        /// # Arguments1395        /// 1396        /// -`contract_address`: Address of the contract to sponsor1397        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1398        /// 1399        #[weight = 0]1400        pub fn set_contract_sponsoring_rate_limit(1401            origin,1402            contract_address: T::AccountId,1403            rate_limit: T::BlockNumber1404        ) -> DispatchResult {1405            let sender = ensure_signed(origin)?;1406            let mut is_owner = false;1407            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1408                let owner = <ContractOwner<T>>::get(&contract_address);1409                is_owner = sender == owner;1410            }1411            ensure!(is_owner, Error::<T>::NoPermission);14121413            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1414            Ok(())1415        }14161417        #[weight = 0]1418        pub fn set_collection_limits(1419            origin,1420            collection_id: u32,1421            limits: CollectionLimits,1422        ) -> DispatchResult {1423            let sender = ensure_signed(origin)?;1424            Self::check_owner_permissions(collection_id, sender.clone())?;14251426            let mut target_collection = <Collection<T>>::get(collection_id);1427            target_collection.limits = limits;1428            <Collection<T>>::insert(collection_id, target_collection);14291430            Ok(())1431        } 1432    }1433}14341435impl<T: Trait> Module<T> {14361437    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14381439        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {14401441            // check token limit and account token limit1442            let total_items: u32 = ItemListIndex::get(collection_id);1443            let account_items: u32 = <AddressTokens<T>>::get(collection_id, sender.clone()).len() as u32;1444            ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1445            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1446            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1447            Self::check_white_list(collection_id, owner)?;1448            Self::check_white_list(collection_id, sender)?;1449        }14501451        Ok(())1452    }14531454    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1455        match target_collection.mode1456        {1457            CollectionMode::NFT => {1458                if let CreateItemData::NFT(data) = data {1459                    // check sizes1460                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1461                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1462                } else {1463                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1464                }1465            },1466            CollectionMode::Fungible(_) => {1467                if let CreateItemData::Fungible(_) = data {1468                } else {1469                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1470                }1471            },1472            CollectionMode::ReFungible(_) => {1473                if let CreateItemData::ReFungible(data) = data {14741475                    // check sizes1476                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1477                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1478                } else {1479                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1480                }1481            },1482            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1483        };14841485        Ok(())1486    }14871488    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1489        match data1490        {1491            CreateItemData::NFT(data) => {1492                let item = NftItemType {1493                    collection: collection_id,1494                    owner,1495                    const_data: data.const_data,1496                    variable_data: data.variable_data1497                };14981499                Self::add_nft_item(item)?;1500            },1501            CreateItemData::Fungible(_) => {1502                let item = FungibleItemType {1503                    collection: collection_id,1504                    owner,1505                    value: (10 as u128).pow(collection.decimal_points as u32)1506                };15071508                Self::add_fungible_item(item)?;1509            },1510            CreateItemData::ReFungible(data) => {1511                let mut owner_list = Vec::new();1512                let value = (10 as u128).pow(collection.decimal_points as u32);1513                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15141515                let item = ReFungibleItemType {1516                    collection: collection_id,1517                    owner: owner_list,1518                    const_data: data.const_data,1519                    variable_data: data.variable_data1520                };15211522                Self::add_refungible_item(item)?;1523            }1524        };15251526        // call event1527        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));15281529        Ok(())1530    }15311532    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1533        let current_index = <ItemListIndex>::get(item.collection)1534            .checked_add(1)1535            .ok_or(Error::<T>::NumOverflow)?;1536        let itemcopy = item.clone();1537        let owner = item.owner.clone();15381539        Self::add_token_index(item.collection, current_index, owner.clone())?;15401541        <ItemListIndex>::insert(item.collection, current_index);1542        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15431544        // Add current block1545        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1546        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1547        1548        // Update balance1549        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1550            .checked_add(item.value)1551            .ok_or(Error::<T>::NumOverflow)?;1552        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15531554        Ok(())1555    }15561557    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1558        let current_index = <ItemListIndex>::get(item.collection)1559            .checked_add(1)1560            .ok_or(Error::<T>::NumOverflow)?;1561        let itemcopy = item.clone();15621563        let value = item.owner.first().unwrap().fraction;1564        let owner = item.owner.first().unwrap().owner.clone();15651566        Self::add_token_index(item.collection, current_index, owner.clone())?;15671568        <ItemListIndex>::insert(item.collection, current_index);1569        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15701571        // Add current block1572        let block_number: T::BlockNumber = 0.into();1573        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15741575        // Update balance1576        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1577            .checked_add(value)1578            .ok_or(Error::<T>::NumOverflow)?;1579        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15801581        Ok(())1582    }15831584    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1585        let current_index = <ItemListIndex>::get(item.collection)1586            .checked_add(1)1587            .ok_or(Error::<T>::NumOverflow)?;15881589        let item_owner = item.owner.clone();1590        let collection_id = item.collection.clone();1591        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15921593        <ItemListIndex>::insert(collection_id, current_index);1594        <NftItemList<T>>::insert(collection_id, current_index, item);15951596        // Add current block1597        let block_number: T::BlockNumber = 0.into();1598        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15991600        // Update balance1601        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1602            .checked_add(1)1603            .ok_or(Error::<T>::NumOverflow)?;1604        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16051606        Ok(())1607    }16081609    fn burn_refungible_item(1610        collection_id: CollectionId,1611        item_id: TokenId,1612        owner: T::AccountId,1613    ) -> DispatchResult {1614        ensure!(1615            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1616            Error::<T>::TokenNotFound1617        );1618        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1619        let item = collection1620            .owner1621            .iter()1622            .filter(|&i| i.owner == owner)1623            .next()1624            .unwrap();1625        Self::remove_token_index(collection_id, item_id, owner.clone())?;16261627        // remove approve list1628        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));16291630        // update balance1631        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1632            .checked_sub(item.fraction)1633            .ok_or(Error::<T>::NumOverflow)?;1634        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16351636        <ReFungibleItemList<T>>::remove(collection_id, item_id);16371638        Ok(())1639    }16401641    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1642        ensure!(1643            <NftItemList<T>>::contains_key(collection_id, item_id),1644            Error::<T>::TokenNotFound1645        );1646        let item = <NftItemList<T>>::get(collection_id, item_id);1647        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16481649        // remove approve list1650        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16511652        // update balance1653        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1654            .checked_sub(1)1655            .ok_or(Error::<T>::NumOverflow)?;1656        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1657        <NftItemList<T>>::remove(collection_id, item_id);16581659        Ok(())1660    }16611662    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1663        ensure!(1664            <FungibleItemList<T>>::contains_key(collection_id, item_id),1665            Error::<T>::TokenNotFound1666        );1667        let item = <FungibleItemList<T>>::get(collection_id, item_id);1668        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16691670        // remove approve list1671        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16721673        // update balance1674        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1675            .checked_sub(item.value)1676            .ok_or(Error::<T>::NumOverflow)?;1677        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16781679        <FungibleItemList<T>>::remove(collection_id, item_id);16801681        Ok(())1682    }16831684    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1685        ensure!(1686            <Collection<T>>::contains_key(collection_id),1687            Error::<T>::CollectionNotFound1688        );1689        Ok(())1690    }16911692    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1693        Self::collection_exists(collection_id)?;16941695        let target_collection = <Collection<T>>::get(collection_id);1696        ensure!(1697            subject == target_collection.owner,1698            Error::<T>::NoPermission1699        );17001701        Ok(())1702    }17031704    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1705        let target_collection = <Collection<T>>::get(collection_id);1706        let mut result: bool = subject == target_collection.owner;1707        let exists = <AdminList<T>>::contains_key(collection_id);17081709        if !result & exists {1710            if <AdminList<T>>::get(collection_id).contains(&subject) {1711                result = true1712            }1713        }17141715        result1716    }17171718    fn check_owner_or_admin_permissions(1719        collection_id: CollectionId,1720        subject: T::AccountId,1721    ) -> DispatchResult {1722        Self::collection_exists(collection_id)?;1723        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17241725        ensure!(1726            result,1727            Error::<T>::NoPermission1728        );1729        Ok(())1730    }17311732    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1733        let target_collection = <Collection<T>>::get(collection_id);17341735        match target_collection.mode {1736            CollectionMode::NFT => {1737                <NftItemList<T>>::get(collection_id, item_id).owner == subject1738            }1739            CollectionMode::Fungible(_) => {1740                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1741            }1742            CollectionMode::ReFungible(_) => {1743                <ReFungibleItemList<T>>::get(collection_id, item_id)1744                    .owner1745                    .iter()1746                    .any(|i| i.owner == subject)1747            }1748            CollectionMode::Invalid => false,1749        }1750    }17511752    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1753        let mes = Error::<T>::AddresNotInWhiteList;1754        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1755        let wl = <WhiteList<T>>::get(collection_id);1756        ensure!(wl.contains(address), mes);17571758        Ok(())1759    }17601761    fn transfer_fungible(1762        collection_id: CollectionId,1763        item_id: TokenId,1764        value: u128,1765        owner: T::AccountId,1766        new_owner: T::AccountId,1767    ) -> DispatchResult {1768        ensure!(1769            <FungibleItemList<T>>::contains_key(collection_id, item_id),1770            Error::<T>::TokenNotFound1771        );17721773        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1774        let amount = full_item.value;17751776        ensure!(amount >= value, Error::<T>::TokenValueTooLow);17771778        // update balance1779        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1780            .checked_sub(value)1781            .ok_or(Error::<T>::NumOverflow)?;1782        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17831784        let mut new_owner_account_id = 0;1785        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1786        if new_owner_items.len() > 0 {1787            new_owner_account_id = new_owner_items[0];1788        }17891790        // transfer1791        if amount == value && new_owner_account_id == 0 {1792            // change owner1793            // new owner do not have account1794            let mut new_full_item = full_item.clone();1795            new_full_item.owner = new_owner.clone();1796            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17971798            // update balance1799            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1800                .checked_add(value)1801                .ok_or(Error::<T>::NumOverflow)?;1802            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18031804            // update index collection1805            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1806        } else {1807            let mut new_full_item = full_item.clone();1808            new_full_item.value -= value;18091810            // separate amount1811            if new_owner_account_id > 0 {1812                // new owner has account1813                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1814                item.value += value;18151816                // update balance1817                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1818                    .checked_add(value)1819                    .ok_or(Error::<T>::NumOverflow)?;1820                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18211822                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1823            } else {1824                // new owner do not have account1825                let item = FungibleItemType {1826                    collection: collection_id,1827                    owner: new_owner.clone(),1828                    value1829                };18301831                Self::add_fungible_item(item)?;1832            }18331834            if amount == value {1835                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18361837                // remove approve list1838                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1839                <FungibleItemList<T>>::remove(collection_id, item_id);1840            }18411842            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1843        }18441845        Ok(())1846    }18471848    fn transfer_refungible(1849        collection_id: CollectionId,1850        item_id: TokenId,1851        value: u128,1852        owner: T::AccountId,1853        new_owner: T::AccountId,1854    ) -> DispatchResult {1855        ensure!(1856            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1857            Error::<T>::TokenNotFound1858        );18591860        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1861        let item = full_item1862            .owner1863            .iter()1864            .filter(|i| i.owner == owner)1865            .next()1866            .ok_or(Error::<T>::NumOverflow)?;1867        let amount = item.fraction;18681869        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18701871        // update balance1872        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1873            .checked_sub(value)1874            .ok_or(Error::<T>::NumOverflow)?;1875        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18761877        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1878            .checked_add(value)1879            .ok_or(Error::<T>::NumOverflow)?;1880        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18811882        let old_owner = item.owner.clone();1883        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);18841885        // transfer1886        if amount == value && !new_owner_has_account {1887            // change owner1888            // new owner do not have account1889            let mut new_full_item = full_item.clone();1890            new_full_item1891                .owner1892                .iter_mut()1893                .find(|i| i.owner == owner)1894                .unwrap()1895                .owner = new_owner.clone();1896            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18971898            // update index collection1899            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1900        } else {1901            let mut new_full_item = full_item.clone();1902            new_full_item1903                .owner1904                .iter_mut()1905                .find(|i| i.owner == owner)1906                .unwrap()1907                .fraction -= value;19081909            // separate amount1910            if new_owner_has_account {1911                // new owner has account1912                new_full_item1913                    .owner1914                    .iter_mut()1915                    .find(|i| i.owner == new_owner)1916                    .unwrap()1917                    .fraction += value;1918            } else {1919                // new owner do not have account1920                new_full_item.owner.push(Ownership {1921                    owner: new_owner.clone(),1922                    fraction: value,1923                });1924                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1925            }19261927            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1928        }19291930        Ok(())1931    }19321933    fn transfer_nft(1934        collection_id: CollectionId,1935        item_id: TokenId,1936        sender: T::AccountId,1937        new_owner: T::AccountId,1938    ) -> DispatchResult {1939        ensure!(1940            <NftItemList<T>>::contains_key(collection_id, item_id),1941            Error::<T>::TokenNotFound1942        );19431944        let mut item = <NftItemList<T>>::get(collection_id, item_id);19451946        ensure!(1947            sender == item.owner,1948            Error::<T>::MustBeTokenOwner1949        );19501951        // update balance1952        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1953            .checked_sub(1)1954            .ok_or(Error::<T>::NumOverflow)?;1955        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19561957        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1958            .checked_add(1)1959            .ok_or(Error::<T>::NumOverflow)?;1960        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19611962        // change owner1963        let old_owner = item.owner.clone();1964        item.owner = new_owner.clone();1965        <NftItemList<T>>::insert(collection_id, item_id, item);19661967        // update index collection1968        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19691970        // reset approved list1971        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1972        Ok(())1973    }1974    1975    fn item_exists(1976        collection_id: CollectionId,1977        item_id: TokenId,1978        mode: &CollectionMode1979    ) -> DispatchResult {1980        match mode {1981            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1982            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1983            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1984            _ => ()1985        };1986        1987        Ok(())1988    }19891990    fn set_re_fungible_variable_data(1991        collection_id: CollectionId,1992        item_id: TokenId,1993        data: Vec<u8>1994    ) -> DispatchResult {1995        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19961997        item.variable_data = data;19981999        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20002001        Ok(())2002    }20032004    fn set_nft_variable_data(2005        collection_id: CollectionId,2006        item_id: TokenId,2007        data: Vec<u8>2008    ) -> DispatchResult {2009        let mut item = <NftItemList<T>>::get(collection_id, item_id);2010        2011        item.variable_data = data;20122013        <NftItemList<T>>::insert(collection_id, item_id, item);2014        2015        Ok(())2016    }20172018    fn init_collection(item: &CollectionType<T::AccountId>) {2019        // check params2020        assert!(2021            item.decimal_points <= MAX_DECIMAL_POINTS,2022            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2023        );2024        assert!(2025            item.name.len() <= 64,2026            "Collection name can not be longer than 63 char"2027        );2028        assert!(2029            item.name.len() <= 256,2030            "Collection description can not be longer than 255 char"2031        );2032        assert!(2033            item.token_prefix.len() <= 16,2034            "Token prefix can not be longer than 15 char"2035        );20362037        // Generate next collection ID2038        let next_id = CreatedCollectionCount::get()2039            .checked_add(1)2040            .unwrap();20412042        CreatedCollectionCount::put(next_id);2043    }20442045    fn init_nft_token(item: &NftItemType<T::AccountId>) {2046        let current_index = <ItemListIndex>::get(item.collection)2047            .checked_add(1)2048            .unwrap();20492050        let item_owner = item.owner.clone();2051        let collection_id = item.collection.clone();2052        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20532054        <ItemListIndex>::insert(collection_id, current_index);20552056        // Update balance2057        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2058            .checked_add(1)2059            .unwrap();2060        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2061    }20622063    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2064        let current_index = <ItemListIndex>::get(item.collection)2065            .checked_add(1)2066            .unwrap();2067        let owner = item.owner.clone();20682069        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20702071        <ItemListIndex>::insert(item.collection, current_index);20722073        // Update balance2074        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2075            .checked_add(item.value)2076            .unwrap();2077        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2078    }20792080    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2081        let current_index = <ItemListIndex>::get(item.collection)2082            .checked_add(1)2083            .unwrap();20842085        let value = item.owner.first().unwrap().fraction;2086        let owner = item.owner.first().unwrap().owner.clone();20872088        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20892090        <ItemListIndex>::insert(item.collection, current_index);20912092        // Update balance2093        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2094            .checked_add(value)2095            .unwrap();2096        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2097    }20982099    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21002101        // add to account limit2102        if <AccountItemCount<T>>::contains_key(owner.clone()) {21032104            // bound Owned tokens by a single address2105            let count = <AccountItemCount<T>>::get(owner.clone());2106            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21072108            <AccountItemCount<T>>::insert(owner.clone(), count2109                .checked_add(1)2110                .ok_or(Error::<T>::NumOverflow)?);2111        }2112        else {2113            <AccountItemCount<T>>::insert(owner.clone(), 1);2114        }21152116        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2117        if list_exists {2118            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2119            let item_contains = list.contains(&item_index.clone());21202121            if !item_contains {2122                list.push(item_index.clone());2123            }21242125            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2126        } else {2127            let mut itm = Vec::new();2128            itm.push(item_index.clone());2129            <AddressTokens<T>>::insert(collection_id, owner, itm);2130            2131        }21322133        Ok(())2134    }21352136    fn remove_token_index(2137        collection_id: CollectionId,2138        item_index: TokenId,2139        owner: T::AccountId,2140    ) -> DispatchResult {21412142        // update counter2143        <AccountItemCount<T>>::insert(owner.clone(), 2144            <AccountItemCount<T>>::get(owner.clone())2145            .checked_sub(1)2146            .ok_or(Error::<T>::NumOverflow)?);214721482149        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2150        if list_exists {2151            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2152            let item_contains = list.contains(&item_index.clone());21532154            if item_contains {2155                list.retain(|&item| item != item_index);2156                <AddressTokens<T>>::insert(collection_id, owner, list);2157            }2158        }21592160        Ok(())2161    }21622163    fn move_token_index(2164        collection_id: CollectionId,2165        item_index: TokenId,2166        old_owner: T::AccountId,2167        new_owner: T::AccountId,2168    ) -> DispatchResult {2169        Self::remove_token_index(collection_id, item_index, old_owner)?;2170        Self::add_token_index(collection_id, item_index, new_owner)?;21712172        Ok(())2173    }2174}21752176////////////////////////////////////////////////////////////////////////////////////////////////////2177// Economic models2178// #region21792180/// Fee multiplier.2181pub type Multiplier = FixedU128;21822183type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2184    <T as system::Trait>::AccountId,2185>>::Balance;2186type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2187    <T as system::Trait>::AccountId,2188>>::NegativeImbalance;21892190/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2191/// in the queue.2192#[derive(Encode, Decode, Clone, Eq, PartialEq)]2193pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2194    #[codec(compact)] BalanceOf<T>2195);21962197impl<T: Trait + Send + Sync> sp_std::fmt::Debug2198    for ChargeTransactionPayment<T>2199{2200    #[cfg(feature = "std")]2201    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2202        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2203    }2204    #[cfg(not(feature = "std"))]2205    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2206        Ok(())2207    }2208}22092210impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2211where2212    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2213    BalanceOf<T>: Send + Sync + FixedPointOperand,2214{2215    /// utility constructor. Used only in client/factory code.2216    pub fn from(fee: BalanceOf<T>) -> Self {2217        Self(fee)2218    }22192220    pub fn traditional_fee(2221        len: usize,2222        info: &DispatchInfoOf<T::Call>,2223        tip: BalanceOf<T>,2224    ) -> BalanceOf<T>2225    where2226        T::Call: Dispatchable<Info = DispatchInfo>,2227    {2228        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2229    }22302231    fn withdraw_fee(2232        &self,2233        who: &T::AccountId,2234        call: &T::Call,2235        info: &DispatchInfoOf<T::Call>,2236        len: usize,2237    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2238        let tip = self.0;22392240        // Set fee based on call type. Creating collection costs 1 Unique.2241        // All other transactions have traditional fees so far2242        // let fee = match call.is_sub_type() {2243        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2244        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2245        //                                                 // _ => <BalanceOf<T>>::from(100)2246        // };2247        let fee = Self::traditional_fee(len, info, tip);22482249        // Determine who is paying transaction fee based on ecnomic model2250        // Parse call to extract collection ID and access collection sponsor2251        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2252            Some(Call::create_item(collection_id, _owner, _properties)) => {22532254                // check free create limit2255                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2256                {2257                    <Collection<T>>::get(collection_id).sponsor2258                } else {2259                    T::AccountId::default()2260                }2261            }2262            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2263                2264                let _collection_limits = <Collection<T>>::get(collection_id).limits;2265                let _collection_mode = <Collection<T>>::get(collection_id).mode;22662267                // sponsor timeout2268                let sponsor_transfer = match _collection_mode {2269                    CollectionMode::NFT => {22702271                        // get correct limit2272                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2273                            _collection_limits.sponsor_transfer_timeout2274                        } else {2275                            ChainLimit::get().nft_sponsor_transfer_timeout2276                        };22772278                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2279                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2280                        let limit_time = basket + limit.into();2281                        if block_number >= limit_time {2282                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2283                            true2284                        }2285                        else {2286                            false2287                        }2288                    }2289                    CollectionMode::Fungible(_) => {22902291                        // get correct limit2292                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2293                            _collection_limits.sponsor_transfer_timeout2294                        } else {2295                            ChainLimit::get().fungible_sponsor_transfer_timeout2296                        };22972298                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2299                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2300                        if basket.iter().any(|i| i.address == _new_owner.clone())2301                        {2302                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2303                            let limit_time = item.start_block + limit.into();2304                            if block_number >= limit_time {2305                                basket.retain(|x| x.address == item.address);2306                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2307                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2308                                true2309                            }2310                            else {2311                                false2312                            }2313                        }2314                        else {2315                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2316                            true2317                        }2318                    }2319                    CollectionMode::ReFungible(_) => {23202321                        // get correct limit2322                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2323                            _collection_limits.sponsor_transfer_timeout2324                        } else {2325                            ChainLimit::get().refungible_sponsor_transfer_timeout2326                        };23272328                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2329                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2330                        let limit_time = basket + limit.into();2331                        if block_number >= limit_time {2332                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2333                            true2334                        } else {2335                            false2336                        }2337                    }2338                    _ => {2339                        false2340                    },2341                };23422343                if !sponsor_transfer {2344                    T::AccountId::default()2345                } else {2346                    <Collection<T>>::get(collection_id).sponsor2347                }2348            }23492350            _ => T::AccountId::default(),2351        };23522353        // Sponsor smart contracts2354        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {23552356            // On instantiation: set the contract owner2357            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {23582359                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2360                    code_hash,2361                    &data,2362                    &who,2363                );2364                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());23652366                T::AccountId::default()2367            },23682369            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2370            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23712372                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23732374                let mut sponsor_transfer = false;2375                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2376                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2377                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2378                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2379                    let limit_time = last_tx_block + rate_limit;23802381                    if block_number >= limit_time {2382                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2383                        sponsor_transfer = true;2384                    }2385                } else {2386                    sponsor_transfer = false;2387                }2388               2389                2390                let mut sp = T::AccountId::default();2391                if sponsor_transfer {2392                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2393                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2394                            sp = called_contract;2395                        }2396                    }2397                }23982399                sp2400            },24012402            _ => sponsor,2403        };24042405        let mut who_pays_fee: T::AccountId = sponsor.clone();2406        if sponsor == T::AccountId::default() {2407            who_pays_fee = who.clone();2408        }24092410        // Only mess with balances if fee is not zero.2411        if fee.is_zero() {2412            return Ok((fee, None));2413        }24142415        match <T as transaction_payment::Trait>::Currency::withdraw(2416            &who_pays_fee,2417            fee,2418            if tip.is_zero() {2419                WithdrawReason::TransactionPayment.into()2420            } else {2421                WithdrawReason::TransactionPayment | WithdrawReason::Tip2422            },2423            ExistenceRequirement::KeepAlive,2424        ) {2425            Ok(imbalance) => Ok((fee, Some(imbalance))),2426            Err(_) => Err(InvalidTransaction::Payment.into()),2427        }2428    }2429}243024312432impl<T: Trait + Send + Sync> SignedExtension2433    for ChargeTransactionPayment<T>2434where2435    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2436    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2437{2438    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2439    type AccountId = T::AccountId;2440    type Call = T::Call;2441    type AdditionalSigned = ();2442    type Pre = (2443        BalanceOf<T>,2444        Self::AccountId,2445        Option<NegativeImbalanceOf<T>>,2446        BalanceOf<T>,2447    );2448    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2449        Ok(())2450    }24512452    fn validate(2453        &self,2454        _who: &Self::AccountId,2455        _call: &Self::Call,2456        _info: &DispatchInfoOf<Self::Call>,2457        _len: usize,2458    ) -> TransactionValidity {2459        Ok(ValidTransaction::default())2460    }24612462    fn pre_dispatch(2463        self,2464        who: &Self::AccountId,2465        call: &Self::Call,2466        info: &DispatchInfoOf<Self::Call>,2467        len: usize,2468    ) -> Result<Self::Pre, TransactionValidityError> {2469        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2470        Ok((self.0, who.clone(), imbalance, fee))2471    }24722473    fn post_dispatch(2474        pre: Self::Pre,2475        info: &DispatchInfoOf<Self::Call>,2476        post_info: &PostDispatchInfoOf<Self::Call>,2477        len: usize,2478        _result: &DispatchResult,2479    ) -> Result<(), TransactionValidityError> {2480        let (tip, who, imbalance, fee) = pre;2481        if let Some(payed) = imbalance {2482            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2483                len as u32, info, post_info, tip,2484            );2485            let refund = fee.saturating_sub(actual_fee);2486            let actual_payment =2487                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2488                    &who, refund,2489                ) {2490                    Ok(refund_imbalance) => {2491                        // The refund cannot be larger than the up front payed max weight.2492                        // `PostDispatchInfo::calc_unspent` guards against such a case.2493                        match payed.offset(refund_imbalance) {2494                            Ok(actual_payment) => actual_payment,2495                            Err(_) => return Err(InvalidTransaction::Payment.into()),2496                        }2497                    }2498                    // We do not recreate the account using the refund. The up front payment2499                    // is gone in that case.2500                    Err(_) => payed,2501                };2502            let imbalances = actual_payment.split(tip);2503            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2504                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2505            );2506        }2507        Ok(())2508    }2509}25102511// #endregion