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, parameter_types, fail,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};252627use 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, Saturating, SignedExtension, Zero,32 },33 transaction_validity::{34 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35 },36 FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849505152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55 Invalid,56 NFT,57 58 Fungible(u32),59 60 ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64 fn into(self) -> u8 {65 match self {66 CollectionMode::Invalid => 0,67 CollectionMode::NFT => 1,68 CollectionMode::Fungible(_) => 2,69 CollectionMode::ReFungible(_) => 3,70 }71 }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77 Normal,78 WhiteList,79}80impl Default for AccessMode {81 fn default() -> Self {82 Self::Normal83 }84}8586impl Default for CollectionMode {87 fn default() -> Self {88 Self::Invalid89 }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95 pub owner: AccountId,96 pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102 pub owner: AccountId,103 pub mode: CollectionMode,104 pub access: AccessMode,105 pub decimal_points: u32,106 pub name: Vec<u16>, 107 pub description: Vec<u16>, 108 pub token_prefix: Vec<u8>, 109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, 112 pub unconfirmed_sponsor: AccountId, 113 pub variable_on_chain_schema: Vec<u8>, 114 pub const_on_chain_schema: Vec<u8>, 115}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 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: u64,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: u64,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: u64,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: u64,163 pub item_id: u64,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, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178 pub collection_numbers_limit: u64,179 pub account_token_ownership_limit: u64,180 pub collections_admins_limit: u64,181 pub custom_data_limit: u32,182183 184 pub nft_sponsor_transfer_timeout: u32,185 pub fungible_sponsor_transfer_timeout: u32,186 pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190 fn create_collection() -> Weight;191 fn destroy_collection() -> Weight;192 fn add_to_white_list() -> Weight;193 fn remove_from_white_list() -> Weight;194 fn set_public_access_mode() -> Weight;195 fn set_mint_permission() -> Weight;196 fn change_collection_owner() -> Weight;197 fn add_collection_admin() -> Weight;198 fn remove_collection_admin() -> Weight;199 fn set_collection_sponsor() -> Weight;200 fn confirm_sponsorship() -> Weight;201 fn remove_collection_sponsor() -> Weight;202 fn create_item(s: usize) -> Weight;203 fn burn_item() -> Weight;204 fn transfer() -> Weight;205 fn approve() -> Weight;206 fn transfer_from() -> Weight;207 fn set_offchain_schema() -> Weight;208 fn set_const_on_chain_schema() -> Weight;209 fn set_variable_on_chain_schema() -> Weight;210 fn set_variable_meta_data() -> Weight;211 212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CreateNftData {217 pub const_data: Vec<u8>,218 pub variable_data: Vec<u8>,219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateFungibleData {224}225226#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228pub struct CreateReFungibleData {229 pub const_data: Vec<u8>,230 pub variable_data: Vec<u8>,231}232233#[derive(Encode, Decode, Debug, Clone, PartialEq)]234#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]235pub enum CreateItemData {236 NFT(CreateNftData),237 Fungible(CreateFungibleData),238 ReFungible(CreateReFungibleData)239}240241impl CreateItemData {242 pub fn len(&self) -> usize {243 let len = match self {244 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),245 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),246 _ => 0247 };248 249 return len;250 }251}252253impl From<CreateNftData> for CreateItemData {254 fn from(item: CreateNftData) -> Self {255 CreateItemData::NFT(item)256 }257}258259impl From<CreateReFungibleData> for CreateItemData {260 fn from(item: CreateReFungibleData) -> Self {261 CreateItemData::ReFungible(item)262 }263}264265impl From<CreateFungibleData> for CreateItemData {266 fn from(item: CreateFungibleData) -> Self {267 CreateItemData::Fungible(item)268 }269}270271pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {272 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;273274 275 type WeightInfo: WeightInfo;276}277278#[cfg(feature = "runtime-benchmarks")]279mod benchmarking;280281282283decl_storage! {284 trait Store for Module<T: Trait> as Nft {285286 287 NextCollectionID: u64;288 CreatedCollectionCount: u64;289 ChainVersion: u64;290 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;291292 293 pub ChainLimit get(fn chain_limit) config(): ChainLimits;294295 296 CollectionCount: u64;297 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;298299 300 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;301 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;302 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;303304 305 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;306307 308 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;309310 311 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;312 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;313 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;314315 316 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;317318 319 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;320 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>>;321 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;322323 324 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;325 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;326 }327 add_extra_genesis {328 build(|config: &GenesisConfig<T>| {329 330 for (_num, _c) in &config.collection {331 <Module<T>>::init_collection(_c);332 }333334 for (_num, _q, _i) in &config.nft_item_id {335 <Module<T>>::init_nft_token(_i);336 }337338 for (_num, _q, _i) in &config.fungible_item_id {339 <Module<T>>::init_fungible_token(_i);340 }341342 for (_num, _q, _i) in &config.refungible_item_id {343 <Module<T>>::init_refungible_token(_i);344 }345 })346 }347}348349decl_event!(350 pub enum Event<T>351 where352 AccountId = <T as system::Trait>::AccountId,353 {354 355 356 357 358 359 360 361 362 363 Created(u64, u8, AccountId),364365 366 367 368 369 370 371 372 ItemCreated(u64, u64),373374 375 376 377 378 379 380 381 ItemDestroyed(u64, u64),382 }383);384385decl_module! {386 pub struct Module<T: Trait> for enum Call where origin: T::Origin {387388 fn deposit_event() = default;389390 fn on_initialize(now: T::BlockNumber) -> Weight {391392 if ChainVersion::get() < 2393 {394 let value = NextCollectionID::get();395 CreatedCollectionCount::put(value);396 ChainVersion::put(2);397 }398399 0400 }401402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 #[weight = T::WeightInfo::create_collection()]419 pub fn create_collection(origin,420 collection_name: Vec<u16>,421 collection_description: Vec<u16>,422 token_prefix: Vec<u8>,423 mode: CollectionMode) -> DispatchResult {424425 426 let who = ensure_signed(origin)?;427428 let decimal_points = match mode {429 CollectionMode::Fungible(points) => points,430 CollectionMode::ReFungible(points) => points,431 _ => 0432 };433434 435 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");436437 438 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");439440 let mut name = collection_name.to_vec();441 name.push(0);442 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");443444 let mut description = collection_description.to_vec();445 description.push(0);446 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");447448 let mut prefix = token_prefix.to_vec();449 prefix.push(0);450 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");451452 453 let next_id = CreatedCollectionCount::get()454 .checked_add(1)455 .expect("collection id error");456457 458 let total = CollectionCount::get()459 .checked_add(1)460 .expect("collection counter error");461462 CreatedCollectionCount::put(next_id);463 CollectionCount::put(total);464465 466 let new_collection = CollectionType {467 owner: who.clone(),468 name: name,469 mode: mode.clone(),470 mint_mode: false,471 access: AccessMode::Normal,472 description: description,473 decimal_points: decimal_points,474 token_prefix: prefix,475 offchain_schema: Vec::new(),476 sponsor: T::AccountId::default(),477 unconfirmed_sponsor: T::AccountId::default(),478 variable_on_chain_schema: Vec::new(),479 const_on_chain_schema: Vec::new(),480 };481482 483 <Collection<T>>::insert(next_id, new_collection);484485 486 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));487488 Ok(())489 }490491 492 493 494 495 496 497 498 499 500 #[weight = T::WeightInfo::destroy_collection()]501 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {502503 let sender = ensure_signed(origin)?;504 Self::check_owner_permissions(collection_id, sender)?;505506 <AddressTokens<T>>::remove_prefix(collection_id);507 <ApprovedList<T>>::remove_prefix(collection_id);508 <Balance<T>>::remove_prefix(collection_id);509 <ItemListIndex>::remove(collection_id);510 <AdminList<T>>::remove(collection_id);511 <Collection<T>>::remove(collection_id);512 <WhiteList<T>>::remove(collection_id);513514 <NftItemList<T>>::remove_prefix(collection_id);515 <FungibleItemList<T>>::remove_prefix(collection_id);516 <ReFungibleItemList<T>>::remove_prefix(collection_id);517518 <NftTransferBasket<T>>::remove_prefix(collection_id);519 <FungibleTransferBasket<T>>::remove_prefix(collection_id);520 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);521522 if CollectionCount::get() > 0523 {524 525 let total = CollectionCount::get()526 .checked_sub(1)527 .expect("collection counter error");528529 CollectionCount::put(total);530 }531532 Ok(())533 }534535 536 537 538 539 540 541 542 543 544 545 546 547 #[weight = T::WeightInfo::add_to_white_list()]548 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{549550 let sender = ensure_signed(origin)?;551 Self::check_owner_or_admin_permissions(collection_id, sender)?;552553 let mut white_list_collection: Vec<T::AccountId>;554 if <WhiteList<T>>::contains_key(collection_id) {555 white_list_collection = <WhiteList<T>>::get(collection_id);556 if !white_list_collection.contains(&address.clone())557 {558 white_list_collection.push(address.clone());559 }560 }561 else {562 white_list_collection = Vec::new();563 white_list_collection.push(address.clone());564 }565566 <WhiteList<T>>::insert(collection_id, white_list_collection);567 Ok(())568 }569570 571 572 573 574 575 576 577 578 579 580 581 582 #[weight = T::WeightInfo::remove_from_white_list()]583 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{584585 let sender = ensure_signed(origin)?;586 Self::check_owner_or_admin_permissions(collection_id, sender)?;587588 if <WhiteList<T>>::contains_key(collection_id) {589 let mut white_list_collection = <WhiteList<T>>::get(collection_id);590 if white_list_collection.contains(&address.clone())591 {592 white_list_collection.retain(|i| *i != address.clone());593 <WhiteList<T>>::insert(collection_id, white_list_collection);594 }595 }596597 Ok(())598 }599600 601 602 603 604 605 606 607 608 609 610 611 #[weight = T::WeightInfo::set_public_access_mode()]612 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult613 {614 let sender = ensure_signed(origin)?;615616 Self::check_owner_permissions(collection_id, sender)?;617 let mut target_collection = <Collection<T>>::get(collection_id);618 target_collection.access = mode;619 <Collection<T>>::insert(collection_id, target_collection);620621 Ok(())622 }623624 625 626 627 628 629 630 631 632 633 634 635 636 637 #[weight = T::WeightInfo::set_mint_permission()]638 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult639 {640 let sender = ensure_signed(origin)?;641642 Self::check_owner_permissions(collection_id, sender)?;643 let mut target_collection = <Collection<T>>::get(collection_id);644 target_collection.mint_mode = mint_permission;645 <Collection<T>>::insert(collection_id, target_collection);646647 Ok(())648 }649650 651 652 653 654 655 656 657 658 659 660 661 #[weight = T::WeightInfo::change_collection_owner()]662 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {663664 let sender = ensure_signed(origin)?;665 Self::check_owner_permissions(collection_id, sender)?;666 let mut target_collection = <Collection<T>>::get(collection_id);667 target_collection.owner = new_owner;668 <Collection<T>>::insert(collection_id, target_collection);669670 Ok(())671 }672673 674 675 676 677 678 679 680 681 682 683 684 685 686 #[weight = T::WeightInfo::add_collection_admin()]687 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {688689 let sender = ensure_signed(origin)?;690 Self::check_owner_or_admin_permissions(collection_id, sender)?;691 let mut admin_arr: Vec<T::AccountId> = Vec::new();692693 if <AdminList<T>>::contains_key(collection_id)694 {695 admin_arr = <AdminList<T>>::get(collection_id);696 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");697 }698699 700 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");701702 admin_arr.push(new_admin_id);703 <AdminList<T>>::insert(collection_id, admin_arr);704705 Ok(())706 }707708 709 710 711 712 713 714 715 716 717 718 719 720 #[weight = T::WeightInfo::remove_collection_admin()]721 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {722723 let sender = ensure_signed(origin)?;724 Self::check_owner_or_admin_permissions(collection_id, sender)?;725726 if <AdminList<T>>::contains_key(collection_id)727 {728 let mut admin_arr = <AdminList<T>>::get(collection_id);729 admin_arr.retain(|i| *i != account_id);730 <AdminList<T>>::insert(collection_id, admin_arr);731 }732733 Ok(())734 }735736 737 738 739 740 741 742 743 744 745 #[weight = T::WeightInfo::set_collection_sponsor()]746 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {747748 let sender = ensure_signed(origin)?;749 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");750751 let mut target_collection = <Collection<T>>::get(collection_id);752 ensure!(sender == target_collection.owner, "You do not own this collection");753754 target_collection.unconfirmed_sponsor = new_sponsor;755 <Collection<T>>::insert(collection_id, target_collection);756757 Ok(())758 }759760 761 762 763 764 765 766 767 #[weight = T::WeightInfo::confirm_sponsorship()]768 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {769770 let sender = ensure_signed(origin)?;771 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");772773 let mut target_collection = <Collection<T>>::get(collection_id);774 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");775776 target_collection.sponsor = target_collection.unconfirmed_sponsor;777 target_collection.unconfirmed_sponsor = T::AccountId::default();778 <Collection<T>>::insert(collection_id, target_collection);779780 Ok(())781 }782783 784 785 786 787 788 789 790 791 792 #[weight = T::WeightInfo::remove_collection_sponsor()]793 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {794795 let sender = ensure_signed(origin)?;796 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");797798 let mut target_collection = <Collection<T>>::get(collection_id);799 ensure!(sender == target_collection.owner, "You do not own this collection");800801 target_collection.sponsor = T::AccountId::default();802 <Collection<T>>::insert(collection_id, target_collection);803804 Ok(())805 }806807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830831 #[weight = T::WeightInfo::create_item(data.len())]832 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {833834 let sender = ensure_signed(origin)?;835 Self::collection_exists(collection_id)?;836 let target_collection = <Collection<T>>::get(collection_id);837838 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {839 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");840 Self::check_white_list(collection_id, &owner)?;841 Self::check_white_list(collection_id, &sender)?;842 }843844 match target_collection.mode845 {846 CollectionMode::NFT => {847 if let CreateItemData::NFT(data) = data {848 849 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");850 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");851 852 853 let item = NftItemType {854 collection: collection_id,855 owner: owner,856 const_data: data.const_data.clone(),857 variable_data: data.variable_data.clone() 858 };859 860 Self::add_nft_item(item)?;861 862 } else {863 fail!("Not NFT item data used to mint in NFT collection.");864 }865 },866 CollectionMode::Fungible(_) => {867 if let CreateItemData::Fungible(_) = data {868 869 let item = FungibleItemType {870 collection: collection_id,871 owner: owner,872 value: (10 as u128).pow(target_collection.decimal_points)873 };874 875 Self::add_fungible_item(item)?;876 } else {877 fail!("Not Fungible item data used to mint in Fungible collection.");878 }879 },880 CollectionMode::ReFungible(_) => {881 if let CreateItemData::ReFungible(data) = data {882 883 884 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");885 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");886 887 let mut owner_list = Vec::new();888 let value = (10 as u128).pow(target_collection.decimal_points);889 owner_list.push(Ownership {owner: owner.clone(), fraction: value});890 891 let item = ReFungibleItemType {892 collection: collection_id,893 owner: owner_list,894 const_data: data.const_data.clone(),895 variable_data: data.variable_data.clone() 896 };897 898 Self::add_refungible_item(item)?;899 } else {900 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");901 }902 },903 _ => { ensure!(1 == 0,"Unexpected collection type."); }904 };905906 907 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));908909 Ok(())910 }911912 913 914 915 916 917 918 919 920 921 922 923 924 925 #[weight = T::WeightInfo::burn_item()]926 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {927928 let sender = ensure_signed(origin)?;929 Self::collection_exists(collection_id)?;930931 932 let target_collection = <Collection<T>>::get(collection_id);933 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||934 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),935 "Only item owner, collection owner and admins can modify item");936937 if target_collection.access == AccessMode::WhiteList {938 Self::check_white_list(collection_id, &sender)?;939 }940941 match target_collection.mode942 {943 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,944 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,945 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,946 _ => ()947 };948949 950 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));951952 Ok(())953 }954955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 #[weight = T::WeightInfo::transfer()]979 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {980981 let sender = ensure_signed(origin)?;982983 984 let target_collection = <Collection<T>>::get(collection_id);985 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||986 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),987 "Only item owner, collection owner and admins can modify item");988989 if target_collection.access == AccessMode::WhiteList {990 Self::check_white_list(collection_id, &sender)?;991 Self::check_white_list(collection_id, &recipient)?;992 }993994 match target_collection.mode995 {996 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,997 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,998 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,999 _ => ()1000 };10011002 Ok(())1003 }10041005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 #[weight = T::WeightInfo::approve()]1021 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10221023 let sender = ensure_signed(origin)?;10241025 1026 let target_collection = <Collection<T>>::get(collection_id);1027 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1028 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1029 "Only item owner, collection owner and admins can approve");10301031 if target_collection.access == AccessMode::WhiteList {1032 Self::check_white_list(collection_id, &sender)?;1033 Self::check_white_list(collection_id, &approved)?;1034 }10351036 1037 let amount = 100000000;10381039 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1040 if list_exists {10411042 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1043 let item_contains = list.iter().any(|i| i.approved == approved);10441045 if !item_contains {1046 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1047 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1048 }1049 } else {10501051 let mut list = Vec::new();1052 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1053 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1054 }10551056 Ok(())1057 }1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 #[weight = T::WeightInfo::transfer_from()]1079 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10801081 let sender = ensure_signed(origin)?;1082 let mut appoved_transfer = false;10831084 1085 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1086 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1087 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1088 if opt_item.is_some()1089 {1090 appoved_transfer = true;1091 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1092 }1093 }10941095 1096 let target_collection = <Collection<T>>::get(collection_id);1097 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1098 "Only item owner, collection owner and admins can modify items");10991100 if target_collection.access == AccessMode::WhiteList {1101 Self::check_white_list(collection_id, &sender)?;1102 Self::check_white_list(collection_id, &recipient)?;1103 }11041105 1106 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1107 .into_iter().filter(|i| i.approved != sender.clone()).collect();1108 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);110911101111 match target_collection.mode1112 {1113 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1114 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1115 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1116 _ => ()1117 };11181119 Ok(())1120 }11211122 1123 #[weight = 0]1124 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11251126 1127 1128 1129 11301131 11321133 11341135 Ok(())1136 }1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 #[weight = T::WeightInfo::set_variable_meta_data()]1151 pub fn set_variable_meta_data (1152 origin,1153 collection_id: u64,1154 item_id: u64,1155 data: Vec<u8>1156 ) -> DispatchResult {1157 let sender = ensure_signed(origin)?;1158 1159 Self::collection_exists(collection_id)?;11601161 1162 let target_collection = <Collection<T>>::get(collection_id);1163 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1164 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1165 "Only item owner, collection owner and admins can modify item");11661167 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11681169 match target_collection.mode1170 {1171 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1172 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1173 _ => ()1174 };11751176 Ok(())1177 }1178 11791180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 #[weight = T::WeightInfo::set_offchain_schema()]1193 pub fn set_offchain_schema(1194 origin,1195 collection_id: u64,1196 schema: Vec<u8>1197 ) -> DispatchResult {1198 let sender = ensure_signed(origin)?;1199 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12001201 let mut target_collection = <Collection<T>>::get(collection_id);1202 target_collection.offchain_schema = schema;1203 <Collection<T>>::insert(collection_id, target_collection);12041205 Ok(())1206 }12071208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 #[weight = T::WeightInfo::set_const_on_chain_schema()]1221 pub fn set_const_on_chain_schema (1222 origin,1223 collection_id: u64,1224 schema: Vec<u8>1225 ) -> DispatchResult {1226 let sender = ensure_signed(origin)?;1227 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12281229 let mut target_collection = <Collection<T>>::get(collection_id);1230 target_collection.const_on_chain_schema = schema;1231 <Collection<T>>::insert(collection_id, target_collection);12321233 Ok(())1234 }12351236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 #[weight = T::WeightInfo::set_const_on_chain_schema()]1249 pub fn set_variable_on_chain_schema (1250 origin,1251 collection_id: u64,1252 schema: Vec<u8>1253 ) -> DispatchResult {1254 let sender = ensure_signed(origin)?;1255 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12561257 let mut target_collection = <Collection<T>>::get(collection_id);1258 target_collection.variable_on_chain_schema = schema;1259 <Collection<T>>::insert(collection_id, target_collection);12601261 Ok(())1262 }12631264 1265 #[weight = 0]1266 pub fn set_chain_limits(1267 origin,1268 limits: ChainLimits1269 ) -> DispatchResult {1270 ensure_root(origin)?;1271 <ChainLimit>::put(limits);1272 Ok(())1273 }12741275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 #[weight = 0]1287 pub fn enable_contract_sponsoring(1288 origin,1289 contract_address: T::AccountId,1290 enable: bool1291 ) -> DispatchResult {1292 let sender = ensure_signed(origin)?;1293 let mut is_owner = false;1294 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1295 let owner = <ContractOwner<T>>::get(&contract_address);1296 is_owner = sender == owner;1297 }1298 ensure!(is_owner, "Only contract owner may call this method");12991300 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1301 Ok(())1302 }13031304 }1305}13061307impl<T: Trait> Module<T> {1308 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1309 let current_index = <ItemListIndex>::get(item.collection)1310 .checked_add(1)1311 .expect("Item list index id error");1312 let itemcopy = item.clone();1313 let owner = item.owner.clone();1314 let value = item.value as u64;13151316 Self::add_token_index(item.collection, current_index, owner.clone())?;13171318 <ItemListIndex>::insert(item.collection, current_index);1319 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13201321 1322 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1323 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1324 1325 1326 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1327 .checked_add(value)1328 .unwrap();1329 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13301331 Ok(())1332 }13331334 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1335 let current_index = <ItemListIndex>::get(item.collection)1336 .checked_add(1)1337 .expect("Item list index id error");1338 let itemcopy = item.clone();13391340 let value = item.owner.first().unwrap().fraction as u64;1341 let owner = item.owner.first().unwrap().owner.clone();13421343 Self::add_token_index(item.collection, current_index, owner.clone())?;13441345 <ItemListIndex>::insert(item.collection, current_index);1346 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13471348 1349 let block_number: T::BlockNumber = 0.into();1350 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13511352 1353 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1354 .checked_add(value)1355 .unwrap();1356 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13571358 Ok(())1359 }13601361 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1362 let current_index = <ItemListIndex>::get(item.collection)1363 .checked_add(1)1364 .expect("Item list index id error");13651366 let item_owner = item.owner.clone();1367 let collection_id = item.collection.clone();1368 Self::add_token_index(collection_id, current_index, item.owner.clone())?;13691370 <ItemListIndex>::insert(collection_id, current_index);1371 <NftItemList<T>>::insert(collection_id, current_index, item);13721373 1374 let block_number: T::BlockNumber = 0.into();1375 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13761377 1378 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1379 .checked_add(1)1380 .unwrap();1381 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13821383 Ok(())1384 }13851386 fn burn_refungible_item(1387 collection_id: u64,1388 item_id: u64,1389 owner: T::AccountId,1390 ) -> DispatchResult {1391 ensure!(1392 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1393 "Item does not exists"1394 );1395 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1396 let item = collection1397 .owner1398 .iter()1399 .filter(|&i| i.owner == owner)1400 .next()1401 .unwrap();1402 Self::remove_token_index(collection_id, item_id, owner.clone())?;14031404 1405 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14061407 1408 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1409 .checked_sub(item.fraction as u64)1410 .unwrap();1411 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14121413 <ReFungibleItemList<T>>::remove(collection_id, item_id);14141415 Ok(())1416 }14171418 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1419 ensure!(1420 <NftItemList<T>>::contains_key(collection_id, item_id),1421 "Item does not exists"1422 );1423 let item = <NftItemList<T>>::get(collection_id, item_id);1424 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14251426 1427 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14281429 1430 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1431 .checked_sub(1)1432 .unwrap();1433 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1434 <NftItemList<T>>::remove(collection_id, item_id);14351436 Ok(())1437 }14381439 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1440 ensure!(1441 <FungibleItemList<T>>::contains_key(collection_id, item_id),1442 "Item does not exists"1443 );1444 let item = <FungibleItemList<T>>::get(collection_id, item_id);1445 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14461447 1448 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14491450 1451 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1452 .checked_sub(item.value as u64)1453 .unwrap();1454 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14551456 <FungibleItemList<T>>::remove(collection_id, item_id);14571458 Ok(())1459 }14601461 fn collection_exists(collection_id: u64) -> DispatchResult {1462 ensure!(1463 <Collection<T>>::contains_key(collection_id),1464 "This collection does not exist"1465 );1466 Ok(())1467 }14681469 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1470 Self::collection_exists(collection_id)?;14711472 let target_collection = <Collection<T>>::get(collection_id);1473 ensure!(1474 subject == target_collection.owner,1475 "You do not own this collection"1476 );14771478 Ok(())1479 }14801481 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1482 let target_collection = <Collection<T>>::get(collection_id);1483 let mut result: bool = subject == target_collection.owner;1484 let exists = <AdminList<T>>::contains_key(collection_id);14851486 if !result & exists {1487 if <AdminList<T>>::get(collection_id).contains(&subject) {1488 result = true1489 }1490 }14911492 result1493 }14941495 fn check_owner_or_admin_permissions(1496 collection_id: u64,1497 subject: T::AccountId,1498 ) -> DispatchResult {1499 Self::collection_exists(collection_id)?;1500 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15011502 ensure!(1503 result,1504 "You do not have permissions to modify this collection"1505 );1506 Ok(())1507 }15081509 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1510 let target_collection = <Collection<T>>::get(collection_id);15111512 match target_collection.mode {1513 CollectionMode::NFT => {1514 <NftItemList<T>>::get(collection_id, item_id).owner == subject1515 }1516 CollectionMode::Fungible(_) => {1517 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1518 }1519 CollectionMode::ReFungible(_) => {1520 <ReFungibleItemList<T>>::get(collection_id, item_id)1521 .owner1522 .iter()1523 .any(|i| i.owner == subject)1524 }1525 CollectionMode::Invalid => false,1526 }1527 }15281529 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1530 let mes = "Address is not in white list";1531 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1532 let wl = <WhiteList<T>>::get(collection_id);1533 ensure!(wl.contains(address), mes);15341535 Ok(())1536 }15371538 fn transfer_fungible(1539 collection_id: u64,1540 item_id: u64,1541 value: u64,1542 owner: T::AccountId,1543 new_owner: T::AccountId,1544 ) -> DispatchResult {1545 ensure!(1546 <FungibleItemList<T>>::contains_key(collection_id, item_id),1547 "Item not exists"1548 );15491550 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1551 let amount = full_item.value;15521553 ensure!(amount >= value.into(), "Item balance not enouth");15541555 1556 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1557 .checked_sub(value)1558 .unwrap();1559 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15601561 let mut new_owner_account_id = 0;1562 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1563 if new_owner_items.len() > 0 {1564 new_owner_account_id = new_owner_items[0];1565 }15661567 let val64 = value.into();15681569 1570 if amount == val64 && new_owner_account_id == 0 {1571 1572 1573 let mut new_full_item = full_item.clone();1574 new_full_item.owner = new_owner.clone();1575 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15761577 1578 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1579 .checked_add(value)1580 .unwrap();1581 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15821583 1584 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1585 } else {1586 let mut new_full_item = full_item.clone();1587 new_full_item.value -= val64;15881589 1590 if new_owner_account_id > 0 {1591 1592 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1593 item.value += val64;15941595 1596 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1597 .checked_add(value)1598 .unwrap();1599 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16001601 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1602 } else {1603 1604 let item = FungibleItemType {1605 collection: collection_id,1606 owner: new_owner.clone(),1607 value: val64,1608 };16091610 Self::add_fungible_item(item)?;1611 }16121613 if amount == val64 {1614 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16151616 1617 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1618 <FungibleItemList<T>>::remove(collection_id, item_id);1619 }16201621 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1622 }16231624 Ok(())1625 }16261627 fn transfer_refungible(1628 collection_id: u64,1629 item_id: u64,1630 value: u64,1631 owner: T::AccountId,1632 new_owner: T::AccountId,1633 ) -> DispatchResult {1634 ensure!(1635 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1636 "Item not exists"1637 );16381639 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1640 let item = full_item1641 .owner1642 .iter()1643 .filter(|i| i.owner == owner)1644 .next()1645 .unwrap();1646 let amount = item.fraction;16471648 ensure!(amount >= value.into(), "Item balance not enouth");16491650 1651 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1652 .checked_sub(value)1653 .unwrap();1654 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16551656 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1657 .checked_add(value)1658 .unwrap();1659 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16601661 let old_owner = item.owner.clone();1662 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1663 let val64 = value.into();16641665 1666 if amount == val64 && !new_owner_has_account {1667 1668 1669 let mut new_full_item = full_item.clone();1670 new_full_item1671 .owner1672 .iter_mut()1673 .find(|i| i.owner == owner)1674 .unwrap()1675 .owner = new_owner.clone();1676 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16771678 1679 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1680 } else {1681 let mut new_full_item = full_item.clone();1682 new_full_item1683 .owner1684 .iter_mut()1685 .find(|i| i.owner == owner)1686 .unwrap()1687 .fraction -= val64;16881689 1690 if new_owner_has_account {1691 1692 new_full_item1693 .owner1694 .iter_mut()1695 .find(|i| i.owner == new_owner)1696 .unwrap()1697 .fraction += val64;1698 } else {1699 1700 new_full_item.owner.push(Ownership {1701 owner: new_owner.clone(),1702 fraction: val64,1703 });1704 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1705 }17061707 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1708 }17091710 Ok(())1711 }17121713 fn transfer_nft(1714 collection_id: u64,1715 item_id: u64,1716 sender: T::AccountId,1717 new_owner: T::AccountId,1718 ) -> DispatchResult {1719 ensure!(1720 <NftItemList<T>>::contains_key(collection_id, item_id),1721 "Item not exists"1722 );17231724 let mut item = <NftItemList<T>>::get(collection_id, item_id);17251726 ensure!(1727 sender == item.owner,1728 "sender parameter and item owner must be equal"1729 );17301731 1732 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1733 .checked_sub(1)1734 .unwrap();1735 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17361737 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1738 .checked_add(1)1739 .unwrap();1740 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17411742 1743 let old_owner = item.owner.clone();1744 item.owner = new_owner.clone();1745 <NftItemList<T>>::insert(collection_id, item_id, item);17461747 1748 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17491750 1751 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1752 Ok(())1753 }1754 1755 fn item_exists(1756 collection_id: u64,1757 item_id: u64,1758 mode: &CollectionMode1759 ) -> DispatchResult {1760 match mode {1761 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1762 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1763 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1764 _ => ()1765 };1766 1767 Ok(())1768 }17691770 fn set_re_fungible_variable_data(1771 collection_id: u64,1772 item_id: u64,1773 data: Vec<u8>1774 ) -> DispatchResult {1775 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17761777 item.variable_data = data;17781779 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17801781 Ok(())1782 }17831784 fn set_nft_variable_data(1785 collection_id: u64,1786 item_id: u64,1787 data: Vec<u8>1788 ) -> DispatchResult {1789 let mut item = <NftItemList<T>>::get(collection_id, item_id);1790 1791 item.variable_data = data;17921793 <NftItemList<T>>::insert(collection_id, item_id, item);1794 1795 Ok(())1796 }17971798 fn init_collection(item: &CollectionType<T::AccountId>) {1799 1800 assert!(1801 item.decimal_points <= 4,1802 "decimal_points parameter must be lower than 4"1803 );1804 assert!(1805 item.name.len() <= 64,1806 "Collection name can not be longer than 63 char"1807 );1808 assert!(1809 item.name.len() <= 256,1810 "Collection description can not be longer than 255 char"1811 );1812 assert!(1813 item.token_prefix.len() <= 16,1814 "Token prefix can not be longer than 15 char"1815 );18161817 1818 let next_id = CreatedCollectionCount::get()1819 .checked_add(1)1820 .expect("collection id error");18211822 CreatedCollectionCount::put(next_id);1823 }18241825 fn init_nft_token(item: &NftItemType<T::AccountId>) {1826 let current_index = <ItemListIndex>::get(item.collection)1827 .checked_add(1)1828 .expect("Item list index id error");18291830 let item_owner = item.owner.clone();1831 let collection_id = item.collection.clone();1832 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18331834 <ItemListIndex>::insert(collection_id, current_index);18351836 1837 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1838 .checked_add(1)1839 .unwrap();1840 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1841 }18421843 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1844 let current_index = <ItemListIndex>::get(item.collection)1845 .checked_add(1)1846 .expect("Item list index id error");1847 let owner = item.owner.clone();1848 let value = item.value as u64;18491850 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18511852 <ItemListIndex>::insert(item.collection, current_index);18531854 1855 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1856 .checked_add(value)1857 .unwrap();1858 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1859 }18601861 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1862 let current_index = <ItemListIndex>::get(item.collection)1863 .checked_add(1)1864 .expect("Item list index id error");18651866 let value = item.owner.first().unwrap().fraction as u64;1867 let owner = item.owner.first().unwrap().owner.clone();18681869 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18701871 <ItemListIndex>::insert(item.collection, current_index);18721873 1874 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1875 .checked_add(value)1876 .unwrap();1877 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1878 }18791880 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18811882 1883 if <AccountItemCount<T>>::contains_key(owner.clone()) {18841885 1886 let count = <AccountItemCount<T>>::get(owner.clone());1887 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18881889 <AccountItemCount<T>>::insert(owner.clone(), 1890 count.checked_add(1).unwrap());1891 }1892 else {1893 <AccountItemCount<T>>::insert(owner.clone(), 1);1894 }18951896 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1897 if list_exists {1898 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1899 let item_contains = list.contains(&item_index.clone());19001901 if !item_contains {1902 list.push(item_index.clone());1903 }19041905 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1906 } else {1907 let mut itm = Vec::new();1908 itm.push(item_index.clone());1909 <AddressTokens<T>>::insert(collection_id, owner, itm);1910 1911 }19121913 Ok(())1914 }19151916 fn remove_token_index(1917 collection_id: u64,1918 item_index: u64,1919 owner: T::AccountId,1920 ) -> DispatchResult {19211922 1923 <AccountItemCount<T>>::insert(owner.clone(), 1924 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());192519261927 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1928 if list_exists {1929 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1930 let item_contains = list.contains(&item_index.clone());19311932 if item_contains {1933 list.retain(|&item| item != item_index);1934 <AddressTokens<T>>::insert(collection_id, owner, list);1935 }1936 }19371938 Ok(())1939 }19401941 fn move_token_index(1942 collection_id: u64,1943 item_index: u64,1944 old_owner: T::AccountId,1945 new_owner: T::AccountId,1946 ) -> DispatchResult {1947 Self::remove_token_index(collection_id, item_index, old_owner)?;1948 Self::add_token_index(collection_id, item_index, new_owner)?;19491950 Ok(())1951 }1952}1953195419551956195719581959pub type Multiplier = FixedU128;19601961type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1962 <T as system::Trait>::AccountId,1963>>::Balance;1964type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1965 <T as system::Trait>::AccountId,1966>>::NegativeImbalance;1967196819691970#[derive(Encode, Decode, Clone, Eq, PartialEq)]1971pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1972 #[codec(compact)] BalanceOf<T>1973);19741975impl<T: Trait + Send + Sync> sp_std::fmt::Debug1976 for ChargeTransactionPayment<T>1977{1978 #[cfg(feature = "std")]1979 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1980 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1981 }1982 #[cfg(not(feature = "std"))]1983 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1984 Ok(())1985 }1986}19871988impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1989where1990 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1991 BalanceOf<T>: Send + Sync + FixedPointOperand,1992{1993 1994 pub fn from(fee: BalanceOf<T>) -> Self {1995 Self(fee)1996 }19971998 pub fn traditional_fee(1999 len: usize,2000 info: &DispatchInfoOf<T::Call>,2001 tip: BalanceOf<T>,2002 ) -> BalanceOf<T>2003 where2004 T::Call: Dispatchable<Info = DispatchInfo>,2005 {2006 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2007 }20082009 fn withdraw_fee(2010 &self,2011 who: &T::AccountId,2012 call: &T::Call,2013 info: &DispatchInfoOf<T::Call>,2014 len: usize,2015 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2016 let tip = self.0;20172018 2019 2020 2021 2022 2023 2024 2025 let fee = Self::traditional_fee(len, info, tip);20262027 2028 2029 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2030 Some(Call::create_item(collection_id, _properties, _owner)) => {2031 <Collection<T>>::get(collection_id).sponsor2032 }2033 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2034 let _collection_mode = <Collection<T>>::get(collection_id).mode;20352036 2037 let sponsor_transfer = match _collection_mode {2038 CollectionMode::NFT => {2039 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2040 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2041 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2042 if block_number >= limit_time {2043 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2044 true2045 }2046 else {2047 false2048 }2049 }2050 CollectionMode::Fungible(_) => {2051 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2052 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2053 if basket.iter().any(|i| i.address == _new_owner.clone())2054 {2055 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2056 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2057 if block_number >= limit_time {2058 basket.retain(|x| x.address == item.address);2059 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2060 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2061 true2062 }2063 else {2064 false2065 }2066 }2067 else {2068 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2069 true2070 }2071 }2072 CollectionMode::ReFungible(_) => {2073 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2074 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2075 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2076 if block_number >= limit_time {2077 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2078 true2079 } else {2080 false2081 }2082 }2083 _ => {2084 false2085 },2086 };20872088 if !sponsor_transfer {2089 T::AccountId::default()2090 } else {2091 <Collection<T>>::get(collection_id).sponsor2092 }2093 }20942095 _ => T::AccountId::default(),2096 };20972098 2099 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21002101 2102 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21032104 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2105 code_hash,2106 &data,2107 &who,2108 );2109 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21102111 T::AccountId::default()2112 },21132114 2115 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21162117 let mut sp = T::AccountId::default();2118 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2119 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2120 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2121 sp = called_contract;2122 }2123 }21242125 sp2126 },21272128 _ => sponsor,2129 };21302131 let mut who_pays_fee: T::AccountId = sponsor.clone();2132 if sponsor == T::AccountId::default() {2133 who_pays_fee = who.clone();2134 }21352136 2137 if fee.is_zero() {2138 return Ok((fee, None));2139 }21402141 match <T as transaction_payment::Trait>::Currency::withdraw(2142 &who_pays_fee,2143 fee,2144 if tip.is_zero() {2145 WithdrawReason::TransactionPayment.into()2146 } else {2147 WithdrawReason::TransactionPayment | WithdrawReason::Tip2148 },2149 ExistenceRequirement::KeepAlive,2150 ) {2151 Ok(imbalance) => Ok((fee, Some(imbalance))),2152 Err(_) => Err(InvalidTransaction::Payment.into()),2153 }2154 }2155}215621572158impl<T: Trait + Send + Sync> SignedExtension2159 for ChargeTransactionPayment<T>2160where2161 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2162 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2163{2164 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2165 type AccountId = T::AccountId;2166 type Call = T::Call;2167 type AdditionalSigned = ();2168 type Pre = (2169 BalanceOf<T>,2170 Self::AccountId,2171 Option<NegativeImbalanceOf<T>>,2172 BalanceOf<T>,2173 );2174 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2175 Ok(())2176 }21772178 fn validate(2179 &self,2180 _who: &Self::AccountId,2181 _call: &Self::Call,2182 _info: &DispatchInfoOf<Self::Call>,2183 _len: usize,2184 ) -> TransactionValidity {2185 Ok(ValidTransaction::default())2186 }21872188 fn pre_dispatch(2189 self,2190 who: &Self::AccountId,2191 call: &Self::Call,2192 info: &DispatchInfoOf<Self::Call>,2193 len: usize,2194 ) -> Result<Self::Pre, TransactionValidityError> {2195 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2196 Ok((self.0, who.clone(), imbalance, fee))2197 }21982199 fn post_dispatch(2200 pre: Self::Pre,2201 info: &DispatchInfoOf<Self::Call>,2202 post_info: &PostDispatchInfoOf<Self::Call>,2203 len: usize,2204 _result: &DispatchResult,2205 ) -> Result<(), TransactionValidityError> {2206 let (tip, who, imbalance, fee) = pre;2207 if let Some(payed) = imbalance {2208 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2209 len as u32, info, post_info, tip,2210 );2211 let refund = fee.saturating_sub(actual_fee);2212 let actual_payment =2213 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2214 &who, refund,2215 ) {2216 Ok(refund_imbalance) => {2217 2218 2219 match payed.offset(refund_imbalance) {2220 Ok(actual_payment) => actual_payment,2221 Err(_) => return Err(InvalidTransaction::Payment.into()),2222 }2223 }2224 2225 2226 Err(_) => payed,2227 };2228 let imbalances = actual_payment.split(tip);2229 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2230 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2231 );2232 }2233 Ok(())2234 }2235}2236223722382239