git.delta.rocks / unique-network / refs/commits / 46fa52585478

difftreelog

source

pallets/nft/src/lib.rs73.9 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,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};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30    traits::{31        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,32        SignedExtension, Zero,33    },34    transaction_validity::{35        InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,36        ValidTransaction,37    },38    FixedPointOperand, FixedU128,39};4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55    Invalid,56    // custom data size57    NFT(u32),58    // decimal points59    Fungible(u32),60    // custom data size and decimal points61    ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65    fn into(self) -> u8 {66        match self {67            CollectionMode::Invalid => 0,68            CollectionMode::NFT(_) => 1,69            CollectionMode::Fungible(_) => 2,70            CollectionMode::ReFungible(_, _) => 3,71        }72    }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78    Normal,79    WhiteList,80}81impl Default for AccessMode {82    fn default() -> Self {83        Self::Normal84    }85}8687impl Default for CollectionMode {88    fn default() -> Self {89        Self::Invalid90    }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96    pub owner: AccountId,97    pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103    pub owner: AccountId,104    pub mode: CollectionMode,105    pub access: AccessMode,106    pub decimal_points: u32,107    pub name: Vec<u16>,        // 64 include null escape char108    pub description: Vec<u16>, // 256 include null escape char109    pub token_prefix: Vec<u8>, // 16 include null escape char110    pub custom_data_size: u32,111    pub mint_mode: bool,112    pub offchain_schema: Vec<u8>,113    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender114    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120    pub admin: AccountId,121    pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: u64,128    pub owner: AccountId,129    pub data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135    pub collection: u64,136    pub owner: AccountId,137    pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143    pub collection: u64,144    pub owner: Vec<Ownership<AccountId>>,145    pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151    pub approved: AccountId,152    pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158    pub sender: AccountId,159    pub recipient: AccountId,160    pub collection_id: u64,161    pub item_id: u64,162    pub amount: u64,163    pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169    pub address: AccountId,170    pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176    pub collection_numbers_limit: u64,177    pub account_token_ownership_limit: u64,178    pub collections_admins_limit: u64,179    pub custom_data_limit: u32,180181    // Timeouts for item types in passed blocks182    pub nft_sponsor_transfer_timeout: u32,183    pub fungible_sponsor_transfer_timeout: u32,184    pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188	fn create_collection() -> Weight;189	fn destroy_collection() -> Weight;190	fn add_to_white_list() -> Weight;191	fn remove_from_white_list() -> Weight;192    fn set_public_access_mode() -> Weight;193    fn set_mint_permission() -> Weight;194    fn change_collection_owner() -> Weight;195    fn add_collection_admin() -> Weight;196    fn remove_collection_admin() -> Weight;197    fn set_collection_sponsor() -> Weight;198    fn confirm_sponsorship() -> Weight;199    fn remove_collection_sponsor() -> Weight;200    fn create_item(s: usize, ) -> Weight;201    fn burn_item() -> Weight;202    fn transfer() -> Weight;203    fn approve() -> Weight;204    fn transfer_from() -> Weight;205    fn set_offchain_schema() -> Weight;206}207208pub trait Trait: system::Trait + Sized  {209    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;210211    /// Weight information for extrinsics in this pallet.212	type WeightInfo: WeightInfo;213}214215#[cfg(feature = "runtime-benchmarks")]216mod benchmarking;217218// #endregion219220decl_storage! {221    trait Store for Module<T: Trait> as Nft {222223        // Private members224        NextCollectionID: u64;225        CreatedCollectionCount: u64;226        ChainVersion: u64;227        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;228229        // Chain limits struct230        pub ChainLimit get(fn chain_limit) config(): ChainLimits;231232        // Bound counters233        CollectionCount: u64;234        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;235236        // Basic collections237        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;238        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;239        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;240241        /// Balance owner per collection map242        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;243244        /// second parameter: item id + owner account id245        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;246247        /// Item collections248        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;249        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;250        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;251252        /// Index list253        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;254255        /// Tokens transfer baskets256        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;257        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;258        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;259260        // Sponsorship261        pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;262        pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;263    }264    add_extra_genesis {265        build(|config: &GenesisConfig<T>| {266            // Modification of storage267            for (_num, _c) in &config.collection {268                <Module<T>>::init_collection(_c);269            }270271            for (_num, _q, _i) in &config.nft_item_id {272                <Module<T>>::init_nft_token(_i);273            }274275            for (_num, _q, _i) in &config.fungible_item_id {276                <Module<T>>::init_fungible_token(_i);277            }278279            for (_num, _q, _i) in &config.refungible_item_id {280                <Module<T>>::init_refungible_token(_i);281            }282        })283    }284}285286decl_event!(287    pub enum Event<T>288    where289        AccountId = <T as system::Trait>::AccountId,290    {291        /// New collection was created292        /// 293        /// # Arguments294        /// 295        /// * collection_id: Globally unique identifier of newly created collection.296        /// 297        /// * mode: [CollectionMode] converted into u8.298        /// 299        /// * account_id: Collection owner.300        Created(u64, u8, AccountId),301302        /// New item was created.303        /// 304        /// # Arguments305        /// 306        /// * collection_id: Id of the collection where item was created.307        /// 308        /// * item_id: Id of an item. Unique within the collection.309        ItemCreated(u64, u64),310311        /// Collection item was burned.312        /// 313        /// # Arguments314        /// 315        /// collection_id.316        /// 317        /// item_id: Identifier of burned NFT.318        ItemDestroyed(u64, u64),319    }320);321322decl_module! {323    pub struct Module<T: Trait> for enum Call where origin: T::Origin {324325        fn deposit_event() = default;326327        fn on_initialize(now: T::BlockNumber) -> Weight {328329            if ChainVersion::get() < 2330            {331                let value = NextCollectionID::get();332                CreatedCollectionCount::put(value);333                ChainVersion::put(2);334            }335336            0337        }338339        /// 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.340        /// 341        /// # Permissions342        /// 343        /// * Anyone.344        /// 345        /// # Arguments346        /// 347        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.348        /// 349        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.350        /// 351        /// * token_prefix: UTF-8 string with token prefix.352        /// 353        /// * mode: [CollectionMode] collection type and type dependent data.354        // returns collection ID355        #[weight = T::WeightInfo::create_collection()]356        pub fn create_collection(origin,357                                 collection_name: Vec<u16>,358                                 collection_description: Vec<u16>,359                                 token_prefix: Vec<u8>,360                                 mode: CollectionMode) -> DispatchResult {361362            // Anyone can create a collection363            let who = ensure_signed(origin)?;364            let custom_data_size = match mode {365                CollectionMode::NFT(size) => {366367                    // bound Custom data size368                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");369                    size370                },371                CollectionMode::ReFungible(size, _) => {372373                    // bound Custom data size374                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");375                    size376                },377                _ => 0378            };379380            let decimal_points = match mode {381                CollectionMode::Fungible(points) => points,382                CollectionMode::ReFungible(_, points) => points,383                _ => 0384            };385386            // bound Total number of collections387            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");388389            // check params390            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");391392            let mut name = collection_name.to_vec();393            name.push(0);394            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");395396            let mut description = collection_description.to_vec();397            description.push(0);398            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");399400            let mut prefix = token_prefix.to_vec();401            prefix.push(0);402            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");403404            // Generate next collection ID405            let next_id = CreatedCollectionCount::get()406                .checked_add(1)407                .expect("collection id error");408409            // bound counter410            let total = CollectionCount::get()411                .checked_add(1)412                .expect("collection counter error");413414            CreatedCollectionCount::put(next_id);415            CollectionCount::put(total);416417            // Create new collection418            let new_collection = CollectionType {419                owner: who.clone(),420                name: name,421                mode: mode.clone(),422                mint_mode: false,423                access: AccessMode::Normal,424                description: description,425                decimal_points: decimal_points,426                token_prefix: prefix,427                offchain_schema: Vec::new(),428                custom_data_size: custom_data_size,429                sponsor: T::AccountId::default(),430                unconfirmed_sponsor: T::AccountId::default(),431            };432433            // Add new collection to map434            <Collection<T>>::insert(next_id, new_collection);435436            // call event437            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));438439            Ok(())440        }441442        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.443        /// 444        /// # Permissions445        /// 446        /// * Collection Owner.447        /// 448        /// # Arguments449        /// 450        /// * collection_id: collection to destroy.451        #[weight = T::WeightInfo::destroy_collection()]452        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {453454            let sender = ensure_signed(origin)?;455            Self::check_owner_permissions(collection_id, sender)?;456457            <AddressTokens<T>>::remove_prefix(collection_id);458            <ApprovedList<T>>::remove_prefix(collection_id);459            <Balance<T>>::remove_prefix(collection_id);460            <ItemListIndex>::remove(collection_id);461            <AdminList<T>>::remove(collection_id);462            <Collection<T>>::remove(collection_id);463            <WhiteList<T>>::remove(collection_id);464465            <NftItemList<T>>::remove_prefix(collection_id);466            <FungibleItemList<T>>::remove_prefix(collection_id);467            <ReFungibleItemList<T>>::remove_prefix(collection_id);468469            <NftTransferBasket<T>>::remove_prefix(collection_id);470            <FungibleTransferBasket<T>>::remove_prefix(collection_id);471            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);472473            if CollectionCount::get() > 0474            {475                // bound couter476                let total = CollectionCount::get()477                    .checked_sub(1)478                    .expect("collection counter error");479480                CollectionCount::put(total);481            }482483            Ok(())484        }485486        /// Add an address to white list.487        /// 488        /// # Permissions489        /// 490        /// * Collection Owner491        /// * Collection Admin492        /// 493        /// # Arguments494        /// 495        /// * collection_id.496        /// 497        /// * address.498        #[weight = T::WeightInfo::add_to_white_list()]499        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{500501            let sender = ensure_signed(origin)?;502            Self::check_owner_or_admin_permissions(collection_id, sender)?;503504            let mut white_list_collection: Vec<T::AccountId>;505            if <WhiteList<T>>::contains_key(collection_id) {506                white_list_collection = <WhiteList<T>>::get(collection_id);507                if !white_list_collection.contains(&address.clone())508                {509                    white_list_collection.push(address.clone());510                }511            }512            else {513                white_list_collection = Vec::new();514                white_list_collection.push(address.clone());515            }516517            <WhiteList<T>>::insert(collection_id, white_list_collection);518            Ok(())519        }520521        /// Remove an address from white list.522        /// 523        /// # Permissions524        /// 525        /// * Collection Owner526        /// * Collection Admin527        /// 528        /// # Arguments529        /// 530        /// * collection_id.531        /// 532        /// * address.533        #[weight = T::WeightInfo::remove_from_white_list()]534        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{535536            let sender = ensure_signed(origin)?;537            Self::check_owner_or_admin_permissions(collection_id, sender)?;538539            if <WhiteList<T>>::contains_key(collection_id) {540                let mut white_list_collection = <WhiteList<T>>::get(collection_id);541                if white_list_collection.contains(&address.clone())542                {543                    white_list_collection.retain(|i| *i != address.clone());544                    <WhiteList<T>>::insert(collection_id, white_list_collection);545                }546            }547548            Ok(())549        }550551        /// Toggle between normal and white list access for the methods with access for `Anyone`.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner.556        /// 557        /// # Arguments558        /// 559        /// * collection_id.560        /// 561        /// * mode: [AccessMode]562        #[weight = T::WeightInfo::set_public_access_mode()]563        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult564        {565            let sender = ensure_signed(origin)?;566567            Self::check_owner_permissions(collection_id, sender)?;568            let mut target_collection = <Collection<T>>::get(collection_id);569            target_collection.access = mode;570            <Collection<T>>::insert(collection_id, target_collection);571572            Ok(())573        }574575        /// Allows Anyone to create tokens if:576        /// * White List is enabled, and577        /// * Address is added to white list, and578        /// * This method was called with True parameter579        /// 580        /// # Permissions581        /// * Collection Owner582        ///583        /// # Arguments584        /// 585        /// * collection_id.586        /// 587        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.588        #[weight = T::WeightInfo::set_mint_permission()]589        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult590        {591            let sender = ensure_signed(origin)?;592593            Self::check_owner_permissions(collection_id, sender)?;594            let mut target_collection = <Collection<T>>::get(collection_id);595            target_collection.mint_mode = mint_permission;596            <Collection<T>>::insert(collection_id, target_collection);597598            Ok(())599        }600601        /// Change the owner of the collection.602        /// 603        /// # Permissions604        /// 605        /// * Collection Owner.606        /// 607        /// # Arguments608        /// 609        /// * collection_id.610        /// 611        /// * new_owner.612        #[weight = T::WeightInfo::change_collection_owner()]613        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {614615            let sender = ensure_signed(origin)?;616            Self::check_owner_permissions(collection_id, sender)?;617            let mut target_collection = <Collection<T>>::get(collection_id);618            target_collection.owner = new_owner;619            <Collection<T>>::insert(collection_id, target_collection);620621            Ok(())622        }623624        /// Adds an admin of the Collection.625        /// 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. 626        /// 627        /// # Permissions628        /// 629        /// * Collection Owner.630        /// * Collection Admin.631        /// 632        /// # Arguments633        /// 634        /// * collection_id: ID of the Collection to add admin for.635        /// 636        /// * new_admin_id: Address of new admin to add.637        #[weight = T::WeightInfo::add_collection_admin()]638        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {639640            let sender = ensure_signed(origin)?;641            Self::check_owner_or_admin_permissions(collection_id, sender)?;642            let mut admin_arr: Vec<T::AccountId> = Vec::new();643644            if <AdminList<T>>::contains_key(collection_id)645            {646                admin_arr = <AdminList<T>>::get(collection_id);647                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");648            }649650            // Number of collection admins651            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");652653            admin_arr.push(new_admin_id);654            <AdminList<T>>::insert(collection_id, admin_arr);655656            Ok(())657        }658659        /// 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.660        ///661        /// # Permissions662        /// 663        /// * Collection Owner.664        /// * Collection Admin.665        /// 666        /// # Arguments667        /// 668        /// * collection_id: ID of the Collection to remove admin for.669        /// 670        /// * account_id: Address of admin to remove.671        #[weight = T::WeightInfo::remove_collection_admin()]672        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {673674            let sender = ensure_signed(origin)?;675            Self::check_owner_or_admin_permissions(collection_id, sender)?;676677            if <AdminList<T>>::contains_key(collection_id)678            {679                let mut admin_arr = <AdminList<T>>::get(collection_id);680                admin_arr.retain(|i| *i != account_id);681                <AdminList<T>>::insert(collection_id, admin_arr);682            }683684            Ok(())685        }686687        /// # Permissions688        /// 689        /// * Collection Owner690        /// 691        /// # Arguments692        /// 693        /// * collection_id.694        /// 695        /// * new_sponsor.696        #[weight = T::WeightInfo::set_collection_sponsor()]697        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {698699            let sender = ensure_signed(origin)?;700            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");701702            let mut target_collection = <Collection<T>>::get(collection_id);703            ensure!(sender == target_collection.owner, "You do not own this collection");704705            target_collection.unconfirmed_sponsor = new_sponsor;706            <Collection<T>>::insert(collection_id, target_collection);707708            Ok(())709        }710711        /// # Permissions712        /// 713        /// * Sponsor.714        /// 715        /// # Arguments716        /// 717        /// * collection_id.718        #[weight = T::WeightInfo::confirm_sponsorship()]719        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {720721            let sender = ensure_signed(origin)?;722            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");723724            let mut target_collection = <Collection<T>>::get(collection_id);725            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");726727            target_collection.sponsor = target_collection.unconfirmed_sponsor;728            target_collection.unconfirmed_sponsor = T::AccountId::default();729            <Collection<T>>::insert(collection_id, target_collection);730731            Ok(())732        }733734        /// Switch back to pay-per-own-transaction model.735        ///736        /// # Permissions737        ///738        /// * Collection owner.739        /// 740        /// # Arguments741        /// 742        /// * collection_id.743        #[weight = T::WeightInfo::remove_collection_sponsor()]744        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {745746            let sender = ensure_signed(origin)?;747            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");748749            let mut target_collection = <Collection<T>>::get(collection_id);750            ensure!(sender == target_collection.owner, "You do not own this collection");751752            target_collection.sponsor = T::AccountId::default();753            <Collection<T>>::insert(collection_id, target_collection);754755            Ok(())756        }757758        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.759        /// 760        /// # Permissions761        /// 762        /// * Collection Owner.763        /// * Collection Admin.764        /// * Anyone if765        ///     * White List is enabled, and766        ///     * Address is added to white list, and767        ///     * MintPermission is enabled (see SetMintPermission method)768        /// 769        /// # Arguments770        /// 771        /// * collection_id: ID of the collection.772        /// 773        /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.774        /// 775        /// * owner: Address, initial owner of the NFT.776        // #[weight =777        // (130_000_000 as Weight)778        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))779        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))780        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]781782        #[weight = T::WeightInfo::create_item(properties.len())]783        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {784785            let sender = ensure_signed(origin)?;786787            Self::collection_exists(collection_id)?;788789            let target_collection = <Collection<T>>::get(collection_id);790791            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;792            Self::validate_create_item_args(&target_collection, &properties)?;793            Self::create_item_no_validation(collection_id, &target_collection, &properties, &owner)?;794795            Ok(())796        }797798        /// This method creates multiple instances of NFT Collection created with CreateCollection method.799        /// 800        /// # Permissions801        /// 802        /// * Collection Owner.803        /// * Collection Admin.804        /// * Anyone if805        ///     * White List is enabled, and806        ///     * Address is added to white list, and807        ///     * MintPermission is enabled (see SetMintPermission method)808        /// 809        /// # Arguments810        /// 811        /// * collection_id: ID of the collection.812        /// 813        /// * properties: Array items properties. Each property is an array of bytes itself, see [create_item].814        /// 815        /// * owner: Address, initial owner of the NFT.816        #[weight = 0]817        pub fn create_multiple_items(origin, collection_id: u64, properties: Vec<Vec<u8>>, owner: T::AccountId) -> DispatchResult {818819            ensure!(properties.len() > 0, "Length of items properties must be greater than 0.");820            let sender = ensure_signed(origin)?;821822            Self::collection_exists(collection_id)?;823            let target_collection = <Collection<T>>::get(collection_id);824825            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;826827            for prop in &properties {828                Self::validate_create_item_args(&target_collection, prop)?;829            }830            for prop in &properties {831                Self::create_item_no_validation(collection_id, &target_collection, prop, &owner)?;832            }833834            Ok(())835        }836837        /// Destroys a concrete instance of NFT.838        /// 839        /// # Permissions840        /// 841        /// * Collection Owner.842        /// * Collection Admin.843        /// * Current NFT Owner.844        /// 845        /// # Arguments846        /// 847        /// * collection_id: ID of the collection.848        /// 849        /// * item_id: ID of NFT to burn.850        #[weight = T::WeightInfo::burn_item()]851        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {852853            let sender = ensure_signed(origin)?;854            Self::collection_exists(collection_id)?;855856            // Transfer permissions check857            let target_collection = <Collection<T>>::get(collection_id);858            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||859                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),860                "Only item owner, collection owner and admins can modify item");861862            if target_collection.access == AccessMode::WhiteList {863                Self::check_white_list(collection_id, &sender)?;864            }865866            match target_collection.mode867            {868                CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,869                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,870                CollectionMode::ReFungible(_, _)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,871                _ => ()872            };873874            // call event875            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));876877            Ok(())878        }879880        /// Change ownership of the token.881        /// 882        /// # Permissions883        /// 884        /// * Collection Owner885        /// * Collection Admin886        /// * Current NFT owner887        ///888        /// # Arguments889        /// 890        /// * recipient: Address of token recipient.891        /// 892        /// * collection_id.893        /// 894        /// * item_id: ID of the item895        ///     * Non-Fungible Mode: Required.896        ///     * Fungible Mode: Ignored.897        ///     * Re-Fungible Mode: Required.898        /// 899        /// * value: Amount to transfer.900        ///     * Non-Fungible Mode: Ignored901        ///     * Fungible Mode: Must specify transferred amount902        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)903        #[weight = T::WeightInfo::transfer()]904        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {905906            let sender = ensure_signed(origin)?;907908            // Transfer permissions check909            let target_collection = <Collection<T>>::get(collection_id);910            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||911                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),912                "Only item owner, collection owner and admins can modify item");913914            if target_collection.access == AccessMode::WhiteList {915                Self::check_white_list(collection_id, &sender)?;916                Self::check_white_list(collection_id, &recipient)?;917            }918919            match target_collection.mode920            {921                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,922                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,923                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,924                _ => ()925            };926927            Ok(())928        }929930        /// Set, change, or remove approved address to transfer the ownership of the NFT.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner935        /// * Collection Admin936        /// * Current NFT owner937        /// 938        /// # Arguments939        /// 940        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).941        /// 942        /// * collection_id.943        /// 944        /// * item_id: ID of the item.945        #[weight = T::WeightInfo::approve()]946        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {947948            let sender = ensure_signed(origin)?;949950            // Transfer permissions check951            let target_collection = <Collection<T>>::get(collection_id);952            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||953                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),954                "Only item owner, collection owner and admins can approve");955956            if target_collection.access == AccessMode::WhiteList {957                Self::check_white_list(collection_id, &sender)?;958                Self::check_white_list(collection_id, &approved)?;959            }960961            // amount param stub962            let amount = 100000000;963964            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));965            if list_exists {966967                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));968                let item_contains = list.iter().any(|i| i.approved == approved);969970                if !item_contains {971                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });972                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);973                }974            } else {975976                let mut list = Vec::new();977                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });978                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);979            }980981            Ok(())982        }983        984        /// 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.985        /// 986        /// # Permissions987        /// * Collection Owner988        /// * Collection Admin989        /// * Current NFT owner990        /// * Address approved by current NFT owner991        /// 992        /// # Arguments993        /// 994        /// * from: Address that owns token.995        /// 996        /// * recipient: Address of token recipient.997        /// 998        /// * collection_id.999        /// 1000        /// * item_id: ID of the item.1001        /// 1002        /// * value: Amount to transfer.1003        #[weight = T::WeightInfo::transfer_from()]1004        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10051006            let sender = ensure_signed(origin)?;1007            let mut appoved_transfer = false;10081009            // Check approve1010            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1011                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1012                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1013                if opt_item.is_some()1014                {1015                    appoved_transfer = true;1016                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1017                }1018            }10191020            // Transfer permissions check1021            let target_collection = <Collection<T>>::get(collection_id);1022            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023                "Only item owner, collection owner and admins can modify items");10241025            if target_collection.access == AccessMode::WhiteList {1026                Self::check_white_list(collection_id, &sender)?;1027                Self::check_white_list(collection_id, &recipient)?;1028            }10291030            // remove approve1031            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1032                .into_iter().filter(|i| i.approved != sender.clone()).collect();1033            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);103410351036            match target_collection.mode1037            {1038                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1039                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1040                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1041                _ => ()1042            };10431044            Ok(())1045        }10461047        ///1048        #[weight = 0]1049        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10501051            // let no_perm_mes = "You do not have permissions to modify this collection";1052            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1053            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1054            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10551056            // // on_nft_received  call10571058            // Self::transfer(origin, collection_id, item_id, new_owner)?;10591060            Ok(())1061        }10621063        /// Set off-chain data schema.1064        /// 1065        /// # Permissions1066        /// 1067        /// * Collection Owner1068        /// * Collection Admin1069        /// 1070        /// # Arguments1071        /// 1072        /// * collection_id.1073        /// 1074        /// * schema: String representing the offchain data schema.1075        #[weight = T::WeightInfo::set_offchain_schema()]1076        pub fn set_offchain_schema(1077            origin,1078            collection_id: u64,1079            schema: Vec<u8>1080        ) -> DispatchResult {1081            let sender = ensure_signed(origin)?;1082            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10831084            let mut target_collection = <Collection<T>>::get(collection_id);1085            target_collection.offchain_schema = schema;1086            <Collection<T>>::insert(collection_id, target_collection);10871088            Ok(())1089        }10901091        // Sudo permissions function1092        #[weight = 0]1093        pub fn set_chain_limits(1094            origin,1095            limits: ChainLimits1096        ) -> DispatchResult {1097            ensure_root(origin)?;1098            <ChainLimit>::put(limits);1099            Ok(())1100        }        1101    }1102}11031104impl<T: Trait> Module<T> {11051106    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {11071108        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1109            ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1110            Self::check_white_list(collection_id, owner)?;1111            Self::check_white_list(collection_id, sender)?;1112        }11131114        Ok(())1115    }11161117    fn validate_create_item_args(collection: &CollectionType<T::AccountId>, properties: &Vec<u8>) -> DispatchResult {11181119        match collection.mode1120        {1121            CollectionMode::NFT(_) => {11221123                // check size1124                ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1125            },1126            CollectionMode::Fungible(_) => {11271128                // check size1129                ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type")1130            },1131            CollectionMode::ReFungible(_, _) => {11321133                // check size1134                ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1135            },1136            _ => {1137                fail!("Unexpected collection mode")1138            }1139        }11401141        Ok(())1142    }11431144    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, properties: &Vec<u8>, owner: &T::AccountId) -> DispatchResult {1145        match collection.mode1146        {1147            CollectionMode::NFT(_) => {11481149                // Create nft item1150                let item = NftItemType {1151                    collection: collection_id,1152                    owner: owner.clone(),1153                    data: properties.clone(),1154                };11551156                Self::add_nft_item(item)?;11571158            },1159            CollectionMode::Fungible(_) => {11601161                let item = FungibleItemType {1162                    collection: collection_id,1163                    owner: owner.clone(),1164                    value: (10 as u128).pow(collection.decimal_points)1165                };11661167                Self::add_fungible_item(item)?;1168            },1169            CollectionMode::ReFungible(_, _) => {11701171                let mut owner_list = Vec::new();1172                let value = (10 as u128).pow(collection.decimal_points);1173                owner_list.push(Ownership {owner: owner.clone(), fraction: value});11741175                let item = ReFungibleItemType {1176                    collection: collection_id,1177                    owner: owner_list,1178                    data: properties.clone()1179                };11801181                Self::add_refungible_item(item)?;1182            },1183            _ => { ensure!(1 == 0,"just error"); }11841185        };11861187        // call event1188        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));11891190        Ok(())1191    }11921193    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1194        let current_index = <ItemListIndex>::get(item.collection)1195            .checked_add(1)1196            .expect("Item list index id error");1197        let itemcopy = item.clone();1198        let owner = item.owner.clone();1199        let value = item.value as u64;12001201        Self::add_token_index(item.collection, current_index, owner.clone())?;12021203        <ItemListIndex>::insert(item.collection, current_index);1204        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12051206        // Add current block1207        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1208        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1209        1210        // Update balance1211        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1212            .checked_add(value)1213            .unwrap();1214        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12151216        Ok(())1217    }12181219    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1220        let current_index = <ItemListIndex>::get(item.collection)1221            .checked_add(1)1222            .expect("Item list index id error");1223        let itemcopy = item.clone();12241225        let value = item.owner.first().unwrap().fraction as u64;1226        let owner = item.owner.first().unwrap().owner.clone();12271228        Self::add_token_index(item.collection, current_index, owner.clone())?;12291230        <ItemListIndex>::insert(item.collection, current_index);1231        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12321233        // Add current block1234        let block_number: T::BlockNumber = 0.into();1235        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);12361237        // Update balance1238        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1239            .checked_add(value)1240            .unwrap();1241        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12421243        Ok(())1244    }12451246    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1247        let current_index = <ItemListIndex>::get(item.collection)1248            .checked_add(1)1249            .expect("Item list index id error");12501251        let item_owner = item.owner.clone();1252        let collection_id = item.collection.clone();1253        Self::add_token_index(collection_id, current_index, item.owner.clone())?;12541255        <ItemListIndex>::insert(collection_id, current_index);1256        <NftItemList<T>>::insert(collection_id, current_index, item);12571258        // Add current block1259        let block_number: T::BlockNumber = 0.into();1260        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12611262        // Update balance1263        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1264            .checked_add(1)1265            .unwrap();1266        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12671268        Ok(())1269    }12701271    fn burn_refungible_item(1272        collection_id: u64,1273        item_id: u64,1274        owner: T::AccountId,1275    ) -> DispatchResult {1276        ensure!(1277            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1278            "Item does not exists"1279        );1280        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1281        let item = collection1282            .owner1283            .iter()1284            .filter(|&i| i.owner == owner)1285            .next()1286            .unwrap();1287        Self::remove_token_index(collection_id, item_id, owner.clone())?;12881289        // remove approve list1290        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12911292        // update balance1293        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1294            .checked_sub(item.fraction as u64)1295            .unwrap();1296        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12971298        <ReFungibleItemList<T>>::remove(collection_id, item_id);12991300        Ok(())1301    }13021303    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1304        ensure!(1305            <NftItemList<T>>::contains_key(collection_id, item_id),1306            "Item does not exists"1307        );1308        let item = <NftItemList<T>>::get(collection_id, item_id);1309        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13101311        // remove approve list1312        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13131314        // update balance1315        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1316            .checked_sub(1)1317            .unwrap();1318        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1319        <NftItemList<T>>::remove(collection_id, item_id);13201321        Ok(())1322    }13231324    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1325        ensure!(1326            <FungibleItemList<T>>::contains_key(collection_id, item_id),1327            "Item does not exists"1328        );1329        let item = <FungibleItemList<T>>::get(collection_id, item_id);1330        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13311332        // remove approve list1333        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13341335        // update balance1336        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1337            .checked_sub(item.value as u64)1338            .unwrap();1339        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13401341        <FungibleItemList<T>>::remove(collection_id, item_id);13421343        Ok(())1344    }13451346    fn collection_exists(collection_id: u64) -> DispatchResult {1347        ensure!(1348            <Collection<T>>::contains_key(collection_id),1349            "This collection does not exist"1350        );1351        Ok(())1352    }13531354    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1355        Self::collection_exists(collection_id)?;13561357        let target_collection = <Collection<T>>::get(collection_id);1358        ensure!(1359            subject == target_collection.owner,1360            "You do not own this collection"1361        );13621363        Ok(())1364    }13651366    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1367        let target_collection = <Collection<T>>::get(collection_id);1368        let mut result: bool = subject == target_collection.owner;1369        let exists = <AdminList<T>>::contains_key(collection_id);13701371        if !result & exists {1372            if <AdminList<T>>::get(collection_id).contains(&subject) {1373                result = true1374            }1375        }13761377        result1378    }13791380    fn check_owner_or_admin_permissions(1381        collection_id: u64,1382        subject: T::AccountId,1383    ) -> DispatchResult {1384        Self::collection_exists(collection_id)?;1385        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13861387        ensure!(1388            result,1389            "You do not have permissions to modify this collection"1390        );1391        Ok(())1392    }13931394    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1395        let target_collection = <Collection<T>>::get(collection_id);13961397        match target_collection.mode {1398            CollectionMode::NFT(_) => {1399                <NftItemList<T>>::get(collection_id, item_id).owner == subject1400            }1401            CollectionMode::Fungible(_) => {1402                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1403            }1404            CollectionMode::ReFungible(_, _) => {1405                <ReFungibleItemList<T>>::get(collection_id, item_id)1406                    .owner1407                    .iter()1408                    .any(|i| i.owner == subject)1409            }1410            CollectionMode::Invalid => false,1411        }1412    }14131414    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1415        let mes = "Address is not in white list";1416        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1417        let wl = <WhiteList<T>>::get(collection_id);1418        ensure!(wl.contains(address), mes);14191420        Ok(())1421    }14221423    fn transfer_fungible(1424        collection_id: u64,1425        item_id: u64,1426        value: u64,1427        owner: T::AccountId,1428        new_owner: T::AccountId,1429    ) -> DispatchResult {1430        ensure!(1431            <FungibleItemList<T>>::contains_key(collection_id, item_id),1432            "Item not exists"1433        );14341435        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1436        let amount = full_item.value;14371438        ensure!(amount >= value.into(), "Item balance not enouth");14391440        // update balance1441        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1442            .checked_sub(value)1443            .unwrap();1444        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);14451446        let mut new_owner_account_id = 0;1447        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1448        if new_owner_items.len() > 0 {1449            new_owner_account_id = new_owner_items[0];1450        }14511452        let val64 = value.into();14531454        // transfer1455        if amount == val64 && new_owner_account_id == 0 {1456            // change owner1457            // new owner do not have account1458            let mut new_full_item = full_item.clone();1459            new_full_item.owner = new_owner.clone();1460            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14611462            // update balance1463            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1464                .checked_add(value)1465                .unwrap();1466            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14671468            // update index collection1469            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1470        } else {1471            let mut new_full_item = full_item.clone();1472            new_full_item.value -= val64;14731474            // separate amount1475            if new_owner_account_id > 0 {1476                // new owner has account1477                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1478                item.value += val64;14791480                // update balance1481                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1482                    .checked_add(value)1483                    .unwrap();1484                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14851486                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1487            } else {1488                // new owner do not have account1489                let item = FungibleItemType {1490                    collection: collection_id,1491                    owner: new_owner.clone(),1492                    value: val64,1493                };14941495                Self::add_fungible_item(item)?;1496            }14971498            if amount == val64 {1499                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15001501                // remove approve list1502                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1503                <FungibleItemList<T>>::remove(collection_id, item_id);1504            }15051506            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1507        }15081509        Ok(())1510    }15111512    fn transfer_refungible(1513        collection_id: u64,1514        item_id: u64,1515        value: u64,1516        owner: T::AccountId,1517        new_owner: T::AccountId,1518    ) -> DispatchResult {1519        ensure!(1520            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1521            "Item not exists"1522        );15231524        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1525        let item = full_item1526            .owner1527            .iter()1528            .filter(|i| i.owner == owner)1529            .next()1530            .unwrap();1531        let amount = item.fraction;15321533        ensure!(amount >= value.into(), "Item balance not enouth");15341535        // update balance1536        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1537            .checked_sub(value)1538            .unwrap();1539        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15401541        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1542            .checked_add(value)1543            .unwrap();1544        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15451546        let old_owner = item.owner.clone();1547        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1548        let val64 = value.into();15491550        // transfer1551        if amount == val64 && !new_owner_has_account {1552            // change owner1553            // new owner do not have account1554            let mut new_full_item = full_item.clone();1555            new_full_item1556                .owner1557                .iter_mut()1558                .find(|i| i.owner == owner)1559                .unwrap()1560                .owner = new_owner.clone();1561            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15621563            // update index collection1564            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1565        } else {1566            let mut new_full_item = full_item.clone();1567            new_full_item1568                .owner1569                .iter_mut()1570                .find(|i| i.owner == owner)1571                .unwrap()1572                .fraction -= val64;15731574            // separate amount1575            if new_owner_has_account {1576                // new owner has account1577                new_full_item1578                    .owner1579                    .iter_mut()1580                    .find(|i| i.owner == new_owner)1581                    .unwrap()1582                    .fraction += val64;1583            } else {1584                // new owner do not have account1585                new_full_item.owner.push(Ownership {1586                    owner: new_owner.clone(),1587                    fraction: val64,1588                });1589                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1590            }15911592            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1593        }15941595        Ok(())1596    }15971598    fn transfer_nft(1599        collection_id: u64,1600        item_id: u64,1601        sender: T::AccountId,1602        new_owner: T::AccountId,1603    ) -> DispatchResult {1604        ensure!(1605            <NftItemList<T>>::contains_key(collection_id, item_id),1606            "Item not exists"1607        );16081609        let mut item = <NftItemList<T>>::get(collection_id, item_id);16101611        ensure!(1612            sender == item.owner,1613            "sender parameter and item owner must be equal"1614        );16151616        // update balance1617        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1618            .checked_sub(1)1619            .unwrap();1620        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16211622        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1623            .checked_add(1)1624            .unwrap();1625        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16261627        // change owner1628        let old_owner = item.owner.clone();1629        item.owner = new_owner.clone();1630        <NftItemList<T>>::insert(collection_id, item_id, item);16311632        // update index collection1633        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;16341635        // reset approved list1636        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1637        Ok(())1638    }16391640    fn init_collection(item: &CollectionType<T::AccountId>) {1641        // check params1642        assert!(1643            item.decimal_points <= 4,1644            "decimal_points parameter must be lower than 4"1645        );1646        assert!(1647            item.name.len() <= 64,1648            "Collection name can not be longer than 63 char"1649        );1650        assert!(1651            item.name.len() <= 256,1652            "Collection description can not be longer than 255 char"1653        );1654        assert!(1655            item.token_prefix.len() <= 16,1656            "Token prefix can not be longer than 15 char"1657        );16581659        // Generate next collection ID1660        let next_id = CreatedCollectionCount::get()1661            .checked_add(1)1662            .expect("collection id error");16631664        CreatedCollectionCount::put(next_id);1665    }16661667    fn init_nft_token(item: &NftItemType<T::AccountId>) {1668        let current_index = <ItemListIndex>::get(item.collection)1669            .checked_add(1)1670            .expect("Item list index id error");16711672        let item_owner = item.owner.clone();1673        let collection_id = item.collection.clone();1674        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16751676        <ItemListIndex>::insert(collection_id, current_index);16771678        // Update balance1679        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1680            .checked_add(1)1681            .unwrap();1682        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1683    }16841685    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1686        let current_index = <ItemListIndex>::get(item.collection)1687            .checked_add(1)1688            .expect("Item list index id error");1689        let owner = item.owner.clone();1690        let value = item.value as u64;16911692        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16931694        <ItemListIndex>::insert(item.collection, current_index);16951696        // Update balance1697        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1698            .checked_add(value)1699            .unwrap();1700        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1701    }17021703    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1704        let current_index = <ItemListIndex>::get(item.collection)1705            .checked_add(1)1706            .expect("Item list index id error");17071708        let value = item.owner.first().unwrap().fraction as u64;1709        let owner = item.owner.first().unwrap().owner.clone();17101711        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();17121713        <ItemListIndex>::insert(item.collection, current_index);17141715        // Update balance1716        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1717            .checked_add(value)1718            .unwrap();1719        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1720    }17211722    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {17231724        // add to account limit1725        if <AccountItemCount<T>>::contains_key(owner.clone()) {17261727            // bound Owned tokens by a single address1728            let count = <AccountItemCount<T>>::get(owner.clone());1729            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");17301731            <AccountItemCount<T>>::insert(owner.clone(), 1732                count.checked_add(1).unwrap());1733        }1734        else {1735            <AccountItemCount<T>>::insert(owner.clone(), 1);1736        }17371738        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1739        if list_exists {1740            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1741            let item_contains = list.contains(&item_index.clone());17421743            if !item_contains {1744                list.push(item_index.clone());1745            }17461747            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1748        } else {1749            let mut itm = Vec::new();1750            itm.push(item_index.clone());1751            <AddressTokens<T>>::insert(collection_id, owner, itm);1752            1753        }17541755        Ok(())1756    }17571758    fn remove_token_index(1759        collection_id: u64,1760        item_index: u64,1761        owner: T::AccountId,1762    ) -> DispatchResult {17631764        // update counter1765        <AccountItemCount<T>>::insert(owner.clone(), 1766            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());176717681769        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1770        if list_exists {1771            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1772            let item_contains = list.contains(&item_index.clone());17731774            if item_contains {1775                list.retain(|&item| item != item_index);1776                <AddressTokens<T>>::insert(collection_id, owner, list);1777            }1778        }17791780        Ok(())1781    }17821783    fn move_token_index(1784        collection_id: u64,1785        item_index: u64,1786        old_owner: T::AccountId,1787        new_owner: T::AccountId,1788    ) -> DispatchResult {1789        Self::remove_token_index(collection_id, item_index, old_owner)?;1790        Self::add_token_index(collection_id, item_index, new_owner)?;17911792        Ok(())1793    }1794}17951796////////////////////////////////////////////////////////////////////////////////////////////////////1797// Economic models1798// #region17991800/// Fee multiplier.1801pub type Multiplier = FixedU128;18021803type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1804    <T as system::Trait>::AccountId,1805>>::Balance;1806type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1807    <T as system::Trait>::AccountId,1808>>::NegativeImbalance;18091810/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1811/// in the queue.1812#[derive(Encode, Decode, Clone, Eq, PartialEq)]1813pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1814    #[codec(compact)] BalanceOf<T>,1815);18161817impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1818    for ChargeTransactionPayment<T>1819{1820    #[cfg(feature = "std")]1821    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1822        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1823    }1824    #[cfg(not(feature = "std"))]1825    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1826        Ok(())1827    }1828}18291830impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1831where1832    T::Call:1833        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1834    BalanceOf<T>: Send + Sync + FixedPointOperand,1835{1836    /// utility constructor. Used only in client/factory code.1837    pub fn from(fee: BalanceOf<T>) -> Self {1838        Self(fee)1839    }18401841    pub fn traditional_fee(1842        len: usize,1843        info: &DispatchInfoOf<T::Call>,1844        tip: BalanceOf<T>,1845    ) -> BalanceOf<T>1846    where1847        T::Call: Dispatchable<Info = DispatchInfo>,1848    {1849        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1850    }18511852    fn withdraw_fee(1853        &self,1854        who: &T::AccountId,1855        call: &T::Call,1856        info: &DispatchInfoOf<T::Call>,1857        len: usize,1858    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1859        let tip = self.0;18601861        // Set fee based on call type. Creating collection costs 1 Unique.1862        // All other transactions have traditional fees so far1863        let fee = match call.is_sub_type() {1864            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1865            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1866                                                        // _ => <BalanceOf<T>>::from(100)1867        };18681869        // Determine who is paying transaction fee based on ecnomic model1870        // Parse call to extract collection ID and access collection sponsor1871        let sponsor: T::AccountId = match call.is_sub_type() {1872            Some(Call::create_item(collection_id, _properties, _owner)) => {1873                <Collection<T>>::get(collection_id).sponsor1874            }1875            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1876                let _collection_mode = <Collection<T>>::get(collection_id).mode;18771878                // sponsor timeout1879                let sponsor_transfer = match _collection_mode {1880                    CollectionMode::NFT(_) => {1881                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1882                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1883                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1884                        if block_number >= limit_time {1885                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1886                            true1887                        }1888                        else {1889                            false1890                        }1891                    }1892                    CollectionMode::Fungible(_) => {1893                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1894                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1895                        if basket.iter().any(|i| i.address == _new_owner.clone())1896                        {1897                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1898                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1899                            if block_number >= limit_time {1900                                basket.retain(|x| x.address == item.address);1901                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1902                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1903                                true1904                            }1905                            else {1906                                false1907                            }1908                        }1909                        else {1910                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1911                            true1912                        }1913                    }1914                    CollectionMode::ReFungible(_, _) => {1915                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1916                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1917                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1918                        if block_number >= limit_time {1919                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1920                            true1921                        } else {1922                            false1923                        }1924                    }1925                    _ => {1926                        false1927                    },1928                };19291930                if !sponsor_transfer {1931                    T::AccountId::default()1932                } else {1933                    <Collection<T>>::get(collection_id).sponsor1934                }1935            }19361937            _ => T::AccountId::default(),1938        };19391940        let mut who_pays_fee: T::AccountId = sponsor.clone();1941        if sponsor == T::AccountId::default() {1942            who_pays_fee = who.clone();1943        }19441945        // Only mess with balances if fee is not zero.1946        if fee.is_zero() {1947            return Ok((fee, None));1948        }19491950        match <T as transaction_payment::Trait>::Currency::withdraw(1951            &who_pays_fee,1952            fee,1953            if tip.is_zero() {1954                WithdrawReason::TransactionPayment.into()1955            } else {1956                WithdrawReason::TransactionPayment | WithdrawReason::Tip1957            },1958            ExistenceRequirement::KeepAlive,1959        ) {1960            Ok(imbalance) => Ok((fee, Some(imbalance))),1961            Err(_) => Err(InvalidTransaction::Payment.into()),1962        }1963    }1964}19651966impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1967    for ChargeTransactionPayment<T>1968where1969    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1970    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1971{1972    const IDENTIFIER: &'static str = "ChargeTransactionPayment";1973    type AccountId = T::AccountId;1974    type Call = T::Call;1975    type AdditionalSigned = ();1976    type Pre = (1977        BalanceOf<T>,1978        Self::AccountId,1979        Option<NegativeImbalanceOf<T>>,1980        BalanceOf<T>,1981    );1982    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1983        Ok(())1984    }19851986    fn validate(1987        &self,1988        who: &Self::AccountId,1989        call: &Self::Call,1990        info: &DispatchInfoOf<Self::Call>,1991        len: usize,1992    ) -> TransactionValidity {1993        let (fee, _) = self.withdraw_fee(who, call, info, len)?;19941995        let mut r = ValidTransaction::default();1996        // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which1997        // will be a bit more than setting the priority to tip. For now, this is enough.1998        r.priority = fee.saturated_into::<TransactionPriority>();1999        Ok(r)2000    }20012002    fn pre_dispatch(2003        self,2004        who: &Self::AccountId,2005        call: &Self::Call,2006        info: &DispatchInfoOf<Self::Call>,2007        len: usize,2008    ) -> Result<Self::Pre, TransactionValidityError> {2009        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2010        Ok((self.0, who.clone(), imbalance, fee))2011    }20122013    fn post_dispatch(2014        pre: Self::Pre,2015        info: &DispatchInfoOf<Self::Call>,2016        post_info: &PostDispatchInfoOf<Self::Call>,2017        len: usize,2018        _result: &DispatchResult,2019    ) -> Result<(), TransactionValidityError> {2020        let (tip, who, imbalance, fee) = pre;2021        if let Some(payed) = imbalance {2022            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2023                len as u32, info, post_info, tip,2024            );2025            let refund = fee.saturating_sub(actual_fee);2026            let actual_payment =2027                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2028                    &who, refund,2029                ) {2030                    Ok(refund_imbalance) => {2031                        // The refund cannot be larger than the up front payed max weight.2032                        // `PostDispatchInfo::calc_unspent` guards against such a case.2033                        match payed.offset(refund_imbalance) {2034                            Ok(actual_payment) => actual_payment,2035                            Err(_) => return Err(InvalidTransaction::Payment.into()),2036                        }2037                    }2038                    // We do not recreate the account using the refund. The up front payment2039                    // is gone in that case.2040                    Err(_) => payed,2041                };2042            let imbalances = actual_payment.split(tip);2043            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2044                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2045            );2046        }2047        Ok(())2048    }2049}2050// #endregion20512052