1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage, decl_error,12 dispatch::DispatchResult,13 ensure, fail, parameter_types,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29 traits::{30 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31 },32 transaction_validity::{33 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34 },35 FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748495051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54 Invalid,55 NFT,56 57 Fungible(u32),58 59 ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63 fn into(self) -> u8 {64 match self {65 CollectionMode::Invalid => 0,66 CollectionMode::NFT => 1,67 CollectionMode::Fungible(_) => 2,68 CollectionMode::ReFungible(_) => 3,69 }70 }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76 Normal,77 WhiteList,78}79impl Default for AccessMode {80 fn default() -> Self {81 Self::Normal82 }83}8485impl Default for CollectionMode {86 fn default() -> Self {87 Self::Invalid88 }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94 pub owner: AccountId,95 pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101 pub owner: AccountId,102 pub mode: CollectionMode,103 pub access: AccessMode,104 pub decimal_points: u32,105 pub name: Vec<u16>, 106 pub description: Vec<u16>, 107 pub token_prefix: Vec<u8>, 108 pub mint_mode: bool,109 pub offchain_schema: Vec<u8>,110 pub sponsor: AccountId, 111 pub unconfirmed_sponsor: AccountId, 112 pub variable_on_chain_schema: Vec<u8>, 113 pub const_on_chain_schema: Vec<u8>, 114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119 pub collection: u64,120 pub owner: AccountId,121 pub const_data: Vec<u8>,122 pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128 pub collection: u64,129 pub owner: AccountId,130 pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136 pub collection: u64,137 pub owner: Vec<Ownership<AccountId>>,138 pub const_data: Vec<u8>,139 pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145 pub approved: AccountId,146 pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152 pub sender: AccountId,153 pub recipient: AccountId,154 pub collection_id: u64,155 pub item_id: u64,156 pub amount: u64,157 pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163 pub address: AccountId,164 pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170 pub collection_numbers_limit: u64,171 pub account_token_ownership_limit: u64,172 pub collections_admins_limit: u64,173 pub custom_data_limit: u32,174175 176 pub nft_sponsor_transfer_timeout: u32,177 pub fungible_sponsor_transfer_timeout: u32,178 pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182 fn create_collection() -> Weight;183 fn destroy_collection() -> Weight;184 fn add_to_white_list() -> Weight;185 fn remove_from_white_list() -> Weight;186 fn set_public_access_mode() -> Weight;187 fn set_mint_permission() -> Weight;188 fn change_collection_owner() -> Weight;189 fn add_collection_admin() -> Weight;190 fn remove_collection_admin() -> Weight;191 fn set_collection_sponsor() -> Weight;192 fn confirm_sponsorship() -> Weight;193 fn remove_collection_sponsor() -> Weight;194 fn create_item(s: usize) -> Weight;195 fn burn_item() -> Weight;196 fn transfer() -> Weight;197 fn approve() -> Weight;198 fn transfer_from() -> Weight;199 fn set_offchain_schema() -> Weight;200 fn set_const_on_chain_schema() -> Weight;201 fn set_variable_on_chain_schema() -> Weight;202 fn set_variable_meta_data() -> Weight;203 fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209 pub const_data: Vec<u8>,210 pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221 pub const_data: Vec<u8>,222 pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228 NFT(CreateNftData),229 Fungible(CreateFungibleData),230 ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234 pub fn len(&self) -> usize {235 let len = match self {236 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238 _ => 0239 };240 241 return len;242 }243}244245impl From<CreateNftData> for CreateItemData {246 fn from(item: CreateNftData) -> Self {247 CreateItemData::NFT(item)248 }249}250251impl From<CreateReFungibleData> for CreateItemData {252 fn from(item: CreateReFungibleData) -> Self {253 CreateItemData::ReFungible(item)254 }255}256257impl From<CreateFungibleData> for CreateItemData {258 fn from(item: CreateFungibleData) -> Self {259 CreateItemData::Fungible(item)260 }261}262263264decl_error! {265 266 pub enum Error for Module<T: Trait> {267 268 TotalCollectionsLimitExceeded,269 270 CollectionDecimalPointLimitExceeded, 271 272 CollectionNameLimitExceeded, 273 274 CollectionDescriptionLimitExceeded, 275 276 CollectionTokenPrefixLimitExceeded,277 278 CollectionNotFound,279 280 TokenNotFound,281 282 NumOverflow, 283 284 AlreadyAdmin, 285 286 NoPermission,287 288 ConfirmUnsetSponsorFail,289 290 PublicMintingNotAllowed,291 292 MustBeTokenOwner,293 294 TokenValueTooLow,295 296 NftSizeLimitExceeded,297 298 FungibleUnexpectedParam,299 300 ApproveNotFound,301 302 TokenValueNotEnough,303 304 ApproveRequired,305 306 AddresNotInWhiteList,307 308 CollectionAdminsLimitExceeded,309 310 AddressOwnershipLimitExceeded,311 312 EmptyArgument,313 }314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319 320 type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326327328decl_storage! {329 trait Store for Module<T: Trait> as Nft {330331 332 NextCollectionID: u64;333 CreatedCollectionCount: u64;334 ChainVersion: u64;335 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337 338 pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340 341 CollectionCount: u64;342 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344 345 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349 350 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352 353 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355 356 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360 361 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363 364 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368 369 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;370 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;371 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;372 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;373 }374 add_extra_genesis {375 build(|config: &GenesisConfig<T>| {376 377 for (_num, _c) in &config.collection {378 <Module<T>>::init_collection(_c);379 }380381 for (_num, _q, _i) in &config.nft_item_id {382 <Module<T>>::init_nft_token(_i);383 }384385 for (_num, _q, _i) in &config.fungible_item_id {386 <Module<T>>::init_fungible_token(_i);387 }388389 for (_num, _q, _i) in &config.refungible_item_id {390 <Module<T>>::init_refungible_token(_i);391 }392 })393 }394}395396decl_event!(397 pub enum Event<T>398 where399 AccountId = <T as system::Trait>::AccountId,400 {401 402 403 404 405 406 407 408 409 410 Created(u64, u8, AccountId),411412 413 414 415 416 417 418 419 ItemCreated(u64, u64),420421 422 423 424 425 426 427 428 ItemDestroyed(u64, u64),429 }430);431432decl_module! {433 pub struct Module<T: Trait> for enum Call where origin: T::Origin {434435 fn deposit_event() = default;436 type Error = Error<T>;437438 fn on_initialize(now: T::BlockNumber) -> Weight {439440 if ChainVersion::get() < 2441 {442 let value = NextCollectionID::get();443 CreatedCollectionCount::put(value);444 ChainVersion::put(2);445 }446447 0448 }449450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 #[weight = T::WeightInfo::create_collection()]467 pub fn create_collection(origin,468 collection_name: Vec<u16>,469 collection_description: Vec<u16>,470 token_prefix: Vec<u8>,471 mode: CollectionMode) -> DispatchResult {472473 474 let who = ensure_signed(origin)?;475476 let decimal_points = match mode {477 CollectionMode::Fungible(points) => points,478 CollectionMode::ReFungible(points) => points,479 _ => 0480 };481482 483 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);484485 486 ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);487488 let mut name = collection_name.to_vec();489 name.push(0);490 ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);491492 let mut description = collection_description.to_vec();493 description.push(0);494 ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);495496 let mut prefix = token_prefix.to_vec();497 prefix.push(0);498 ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);499500 501 let next_id = CreatedCollectionCount::get()502 .checked_add(1)503 .ok_or(Error::<T>::NumOverflow)?;504505 506 let total = CollectionCount::get()507 .checked_add(1)508 .ok_or(Error::<T>::NumOverflow)?;509510 CreatedCollectionCount::put(next_id);511 CollectionCount::put(total);512513 514 let new_collection = CollectionType {515 owner: who.clone(),516 name: name,517 mode: mode.clone(),518 mint_mode: false,519 access: AccessMode::Normal,520 description: description,521 decimal_points: decimal_points,522 token_prefix: prefix,523 offchain_schema: Vec::new(),524 sponsor: T::AccountId::default(),525 unconfirmed_sponsor: T::AccountId::default(),526 variable_on_chain_schema: Vec::new(),527 const_on_chain_schema: Vec::new(),528 };529530 531 <Collection<T>>::insert(next_id, new_collection);532533 534 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));535536 Ok(())537 }538539 540 541 542 543 544 545 546 547 548 #[weight = T::WeightInfo::destroy_collection()]549 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {550551 let sender = ensure_signed(origin)?;552 Self::check_owner_permissions(collection_id, sender)?;553554 <AddressTokens<T>>::remove_prefix(collection_id);555 <ApprovedList<T>>::remove_prefix(collection_id);556 <Balance<T>>::remove_prefix(collection_id);557 <ItemListIndex>::remove(collection_id);558 <AdminList<T>>::remove(collection_id);559 <Collection<T>>::remove(collection_id);560 <WhiteList<T>>::remove(collection_id);561562 <NftItemList<T>>::remove_prefix(collection_id);563 <FungibleItemList<T>>::remove_prefix(collection_id);564 <ReFungibleItemList<T>>::remove_prefix(collection_id);565566 <NftTransferBasket<T>>::remove_prefix(collection_id);567 <FungibleTransferBasket<T>>::remove_prefix(collection_id);568 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);569570 if CollectionCount::get() > 0571 {572 573 let total = CollectionCount::get()574 .checked_sub(1)575 .ok_or(Error::<T>::NumOverflow)?;576577 CollectionCount::put(total);578 }579580 Ok(())581 }582583 584 585 586 587 588 589 590 591 592 593 594 595 #[weight = T::WeightInfo::add_to_white_list()]596 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{597598 let sender = ensure_signed(origin)?;599 Self::check_owner_or_admin_permissions(collection_id, sender)?;600601 let mut white_list_collection: Vec<T::AccountId>;602 if <WhiteList<T>>::contains_key(collection_id) {603 white_list_collection = <WhiteList<T>>::get(collection_id);604 if !white_list_collection.contains(&address.clone())605 {606 white_list_collection.push(address.clone());607 }608 }609 else {610 white_list_collection = Vec::new();611 white_list_collection.push(address.clone());612 }613614 <WhiteList<T>>::insert(collection_id, white_list_collection);615 Ok(())616 }617618 619 620 621 622 623 624 625 626 627 628 629 630 #[weight = T::WeightInfo::remove_from_white_list()]631 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{632633 let sender = ensure_signed(origin)?;634 Self::check_owner_or_admin_permissions(collection_id, sender)?;635636 if <WhiteList<T>>::contains_key(collection_id) {637 let mut white_list_collection = <WhiteList<T>>::get(collection_id);638 if white_list_collection.contains(&address.clone())639 {640 white_list_collection.retain(|i| *i != address.clone());641 <WhiteList<T>>::insert(collection_id, white_list_collection);642 }643 }644645 Ok(())646 }647648 649 650 651 652 653 654 655 656 657 658 659 #[weight = T::WeightInfo::set_public_access_mode()]660 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult661 {662 let sender = ensure_signed(origin)?;663664 Self::check_owner_permissions(collection_id, sender)?;665 let mut target_collection = <Collection<T>>::get(collection_id);666 target_collection.access = mode;667 <Collection<T>>::insert(collection_id, target_collection);668669 Ok(())670 }671672 673 674 675 676 677 678 679 680 681 682 683 684 685 #[weight = T::WeightInfo::set_mint_permission()]686 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult687 {688 let sender = ensure_signed(origin)?;689690 Self::check_owner_permissions(collection_id, sender)?;691 let mut target_collection = <Collection<T>>::get(collection_id);692 target_collection.mint_mode = mint_permission;693 <Collection<T>>::insert(collection_id, target_collection);694695 Ok(())696 }697698 699 700 701 702 703 704 705 706 707 708 709 #[weight = T::WeightInfo::change_collection_owner()]710 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {711712 let sender = ensure_signed(origin)?;713 Self::check_owner_permissions(collection_id, sender)?;714 let mut target_collection = <Collection<T>>::get(collection_id);715 target_collection.owner = new_owner;716 <Collection<T>>::insert(collection_id, target_collection);717718 Ok(())719 }720721 722 723 724 725 726 727 728 729 730 731 732 733 734 #[weight = T::WeightInfo::add_collection_admin()]735 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {736737 let sender = ensure_signed(origin)?;738 Self::check_owner_or_admin_permissions(collection_id, sender)?;739 let mut admin_arr: Vec<T::AccountId> = Vec::new();740741 if <AdminList<T>>::contains_key(collection_id)742 {743 admin_arr = <AdminList<T>>::get(collection_id);744 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);745 }746747 748 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);749750 admin_arr.push(new_admin_id);751 <AdminList<T>>::insert(collection_id, admin_arr);752753 Ok(())754 }755756 757 758 759 760 761 762 763 764 765 766 767 768 #[weight = T::WeightInfo::remove_collection_admin()]769 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {770771 let sender = ensure_signed(origin)?;772 Self::check_owner_or_admin_permissions(collection_id, sender)?;773774 if <AdminList<T>>::contains_key(collection_id)775 {776 let mut admin_arr = <AdminList<T>>::get(collection_id);777 admin_arr.retain(|i| *i != account_id);778 <AdminList<T>>::insert(collection_id, admin_arr);779 }780781 Ok(())782 }783784 785 786 787 788 789 790 791 792 793 #[weight = T::WeightInfo::set_collection_sponsor()]794 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {795796 let sender = ensure_signed(origin)?;797 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);798799 let mut target_collection = <Collection<T>>::get(collection_id);800 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);801802 target_collection.unconfirmed_sponsor = new_sponsor;803 <Collection<T>>::insert(collection_id, target_collection);804805 Ok(())806 }807808 809 810 811 812 813 814 815 #[weight = T::WeightInfo::confirm_sponsorship()]816 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {817818 let sender = ensure_signed(origin)?;819 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);820821 let mut target_collection = <Collection<T>>::get(collection_id);822 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);823824 target_collection.sponsor = target_collection.unconfirmed_sponsor;825 target_collection.unconfirmed_sponsor = T::AccountId::default();826 <Collection<T>>::insert(collection_id, target_collection);827828 Ok(())829 }830831 832 833 834 835 836 837 838 839 840 #[weight = T::WeightInfo::remove_collection_sponsor()]841 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {842843 let sender = ensure_signed(origin)?;844 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);845846 let mut target_collection = <Collection<T>>::get(collection_id);847 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);848849 target_collection.sponsor = T::AccountId::default();850 <Collection<T>>::insert(collection_id, target_collection);851852 Ok(())853 }854855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878879 #[weight = T::WeightInfo::create_item(data.len())]880 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {881882 let sender = ensure_signed(origin)?;883884 Self::collection_exists(collection_id)?;885886 let target_collection = <Collection<T>>::get(collection_id);887888 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;889 Self::validate_create_item_args(&target_collection, &data)?;890 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;891892 Ok(())893 }894895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 #[weight = T::WeightInfo::create_item(items_data.into_iter()914 .map(|data| { data.len() })915 .sum())]916 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {917918 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);919 let sender = ensure_signed(origin)?;920921 Self::collection_exists(collection_id)?;922 let target_collection = <Collection<T>>::get(collection_id);923924 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;925926 for data in &items_data {927 Self::validate_create_item_args(&target_collection, data)?;928 }929 for data in &items_data {930 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;931 }932933 Ok(())934 }935936 937 938 939 940 941 942 943 944 945 946 947 948 949 #[weight = T::WeightInfo::burn_item()]950 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {951952 let sender = ensure_signed(origin)?;953 Self::collection_exists(collection_id)?;954955 956 let target_collection = <Collection<T>>::get(collection_id);957 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||958 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),959 Error::<T>::NoPermission);960961 if target_collection.access == AccessMode::WhiteList {962 Self::check_white_list(collection_id, &sender)?;963 }964965 match target_collection.mode966 {967 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,968 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,969 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,970 _ => ()971 };972973 974 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));975976 Ok(())977 }978979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 #[weight = T::WeightInfo::transfer()]1003 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10041005 let sender = ensure_signed(origin)?;10061007 1008 let target_collection = <Collection<T>>::get(collection_id);1009 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1010 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1011 Error::<T>::NoPermission);10121013 if target_collection.access == AccessMode::WhiteList {1014 Self::check_white_list(collection_id, &sender)?;1015 Self::check_white_list(collection_id, &recipient)?;1016 }10171018 match target_collection.mode1019 {1020 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1021 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1022 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1023 _ => ()1024 };10251026 Ok(())1027 }10281029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 #[weight = T::WeightInfo::approve()]1045 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10461047 let sender = ensure_signed(origin)?;10481049 1050 let target_collection = <Collection<T>>::get(collection_id);1051 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1052 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1053 Error::<T>::NoPermission);10541055 if target_collection.access == AccessMode::WhiteList {1056 Self::check_white_list(collection_id, &sender)?;1057 Self::check_white_list(collection_id, &approved)?;1058 }10591060 1061 let amount = 100000000;10621063 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1064 if list_exists {10651066 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1067 let item_contains = list.iter().any(|i| i.approved == approved);10681069 if !item_contains {1070 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1071 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1072 }1073 } else {10741075 let mut list = Vec::new();1076 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1077 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1078 }10791080 Ok(())1081 }1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 #[weight = T::WeightInfo::transfer_from()]1103 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11041105 let sender = ensure_signed(origin)?;1106 let mut appoved_transfer = false;11071108 1109 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1110 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1111 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1112 if opt_item.is_some()1113 {1114 appoved_transfer = true;1115 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1116 }1117 }11181119 1120 let target_collection = <Collection<T>>::get(collection_id);1121 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1122 Error::<T>::NoPermission);11231124 if target_collection.access == AccessMode::WhiteList {1125 Self::check_white_list(collection_id, &sender)?;1126 Self::check_white_list(collection_id, &recipient)?;1127 }11281129 1130 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1131 .into_iter().filter(|i| i.approved != sender.clone()).collect();1132 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113311341135 match target_collection.mode1136 {1137 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1138 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1139 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1140 _ => ()1141 };11421143 Ok(())1144 }11451146 1147 #[weight = 0]1148 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11491150 1151 1152 1153 11541155 11561157 11581159 Ok(())1160 }11611162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 #[weight = T::WeightInfo::set_variable_meta_data()]1175 pub fn set_variable_meta_data (1176 origin,1177 collection_id: u64,1178 item_id: u64,1179 data: Vec<u8>1180 ) -> DispatchResult {1181 let sender = ensure_signed(origin)?;1182 1183 Self::collection_exists(collection_id)?;11841185 1186 let target_collection = <Collection<T>>::get(collection_id);1187 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1188 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1189 Error::<T>::NoPermission);11901191 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11921193 match target_collection.mode1194 {1195 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1196 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1197 _ => ()1198 };11991200 Ok(())1201 }1202 12031204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 #[weight = T::WeightInfo::set_offchain_schema()]1217 pub fn set_offchain_schema(1218 origin,1219 collection_id: u64,1220 schema: Vec<u8>1221 ) -> DispatchResult {1222 let sender = ensure_signed(origin)?;1223 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12241225 let mut target_collection = <Collection<T>>::get(collection_id);1226 target_collection.offchain_schema = schema;1227 <Collection<T>>::insert(collection_id, target_collection);12281229 Ok(())1230 }12311232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 #[weight = T::WeightInfo::set_const_on_chain_schema()]1245 pub fn set_const_on_chain_schema (1246 origin,1247 collection_id: u64,1248 schema: Vec<u8>1249 ) -> DispatchResult {1250 let sender = ensure_signed(origin)?;1251 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12521253 let mut target_collection = <Collection<T>>::get(collection_id);1254 target_collection.const_on_chain_schema = schema;1255 <Collection<T>>::insert(collection_id, target_collection);12561257 Ok(())1258 }12591260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 #[weight = T::WeightInfo::set_const_on_chain_schema()]1273 pub fn set_variable_on_chain_schema (1274 origin,1275 collection_id: u64,1276 schema: Vec<u8>1277 ) -> DispatchResult {1278 let sender = ensure_signed(origin)?;1279 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12801281 let mut target_collection = <Collection<T>>::get(collection_id);1282 target_collection.variable_on_chain_schema = schema;1283 <Collection<T>>::insert(collection_id, target_collection);12841285 Ok(())1286 }12871288 1289 #[weight = 0]1290 pub fn set_chain_limits(1291 origin,1292 limits: ChainLimits1293 ) -> DispatchResult {1294 ensure_root(origin)?;1295 <ChainLimit>::put(limits);1296 Ok(())1297 }12981299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 #[weight = T::WeightInfo::enable_contract_sponsoring()]1311 pub fn enable_contract_sponsoring(1312 origin,1313 contract_address: T::AccountId,1314 enable: bool1315 ) -> DispatchResult {13161317 let sender = ensure_signed(origin)?;13181319 #[cfg(feature = "runtime-benchmarks")]1320 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13211322 let mut is_owner = false;1323 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1324 let owner = <ContractOwner<T>>::get(&contract_address);1325 is_owner = sender == owner;1326 }1327 ensure!(is_owner, Error::<T>::NoPermission);13281329 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1330 Ok(())1331 }13321333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 #[weight = 0]1351 pub fn set_contract_sponsoring_rate_limit(1352 origin,1353 contract_address: T::AccountId,1354 rate_limit: T::BlockNumber1355 ) -> DispatchResult {1356 let sender = ensure_signed(origin)?;1357 let mut is_owner = false;1358 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1359 let owner = <ContractOwner<T>>::get(&contract_address);1360 is_owner = sender == owner;1361 }1362 ensure!(is_owner, Error::<T>::NoPermission);13631364 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1365 Ok(())1366 }13671368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 }1380}13811382impl<T: Trait> Module<T> {13831384 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13851386 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1387 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1388 Self::check_white_list(collection_id, owner)?;1389 Self::check_white_list(collection_id, sender)?;1390 }13911392 Ok(())1393 }13941395 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1396 match target_collection.mode1397 {1398 CollectionMode::NFT => {1399 if let CreateItemData::NFT(data) = data {1400 1401 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1402 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1403 } else {1404 fail!("Not NFT item data used to mint in NFT collection.");1405 }1406 },1407 CollectionMode::Fungible(_) => {1408 if let CreateItemData::Fungible(_) = data {1409 } else {1410 fail!("Not Fungible item data used to mint in Fungible collection.");1411 }1412 },1413 CollectionMode::ReFungible(_) => {1414 if let CreateItemData::ReFungible(data) = data {14151416 1417 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1418 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1419 } else {1420 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1421 }1422 },1423 _ => { fail!("Unexpected collection type."); }1424 };14251426 Ok(())1427 }14281429 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1430 match data1431 {1432 CreateItemData::NFT(data) => {1433 let item = NftItemType {1434 collection: collection_id,1435 owner,1436 const_data: data.const_data,1437 variable_data: data.variable_data1438 };14391440 Self::add_nft_item(item)?;1441 },1442 CreateItemData::Fungible(_) => {1443 let item = FungibleItemType {1444 collection: collection_id,1445 owner,1446 value: (10 as u128).pow(collection.decimal_points)1447 };14481449 Self::add_fungible_item(item)?;1450 },1451 CreateItemData::ReFungible(data) => {1452 let mut owner_list = Vec::new();1453 let value = (10 as u128).pow(collection.decimal_points);1454 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14551456 let item = ReFungibleItemType {1457 collection: collection_id,1458 owner: owner_list,1459 const_data: data.const_data,1460 variable_data: data.variable_data1461 };14621463 Self::add_refungible_item(item)?;1464 }1465 };146614671468 1469 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14701471 Ok(())1472 }14731474 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1475 let current_index = <ItemListIndex>::get(item.collection)1476 .checked_add(1)1477 .ok_or(Error::<T>::NumOverflow)?;1478 let itemcopy = item.clone();1479 let owner = item.owner.clone();1480 let value = item.value as u64;14811482 Self::add_token_index(item.collection, current_index, owner.clone())?;14831484 <ItemListIndex>::insert(item.collection, current_index);1485 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14861487 1488 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1489 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1490 1491 1492 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1493 .checked_add(value)1494 .ok_or(Error::<T>::NumOverflow)?;1495 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14961497 Ok(())1498 }14991500 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1501 let current_index = <ItemListIndex>::get(item.collection)1502 .checked_add(1)1503 .ok_or(Error::<T>::NumOverflow)?;1504 let itemcopy = item.clone();15051506 let value = item.owner.first().unwrap().fraction as u64;1507 let owner = item.owner.first().unwrap().owner.clone();15081509 Self::add_token_index(item.collection, current_index, owner.clone())?;15101511 <ItemListIndex>::insert(item.collection, current_index);1512 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15131514 1515 let block_number: T::BlockNumber = 0.into();1516 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15171518 1519 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1520 .checked_add(value)1521 .ok_or(Error::<T>::NumOverflow)?;1522 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15231524 Ok(())1525 }15261527 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1528 let current_index = <ItemListIndex>::get(item.collection)1529 .checked_add(1)1530 .ok_or(Error::<T>::NumOverflow)?;15311532 let item_owner = item.owner.clone();1533 let collection_id = item.collection.clone();1534 Self::add_token_index(collection_id, current_index, item.owner.clone())?;15351536 <ItemListIndex>::insert(collection_id, current_index);1537 <NftItemList<T>>::insert(collection_id, current_index, item);15381539 1540 let block_number: T::BlockNumber = 0.into();1541 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15421543 1544 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1545 .checked_add(1)1546 .ok_or(Error::<T>::NumOverflow)?;1547 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15481549 Ok(())1550 }15511552 fn burn_refungible_item(1553 collection_id: u64,1554 item_id: u64,1555 owner: T::AccountId,1556 ) -> DispatchResult {1557 ensure!(1558 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1559 Error::<T>::TokenNotFound1560 );1561 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1562 let item = collection1563 .owner1564 .iter()1565 .filter(|&i| i.owner == owner)1566 .next()1567 .unwrap();1568 Self::remove_token_index(collection_id, item_id, owner.clone())?;15691570 1571 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15721573 1574 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1575 .checked_sub(item.fraction as u64)1576 .ok_or(Error::<T>::NumOverflow)?;1577 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15781579 <ReFungibleItemList<T>>::remove(collection_id, item_id);15801581 Ok(())1582 }15831584 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1585 ensure!(1586 <NftItemList<T>>::contains_key(collection_id, item_id),1587 Error::<T>::TokenNotFound1588 );1589 let item = <NftItemList<T>>::get(collection_id, item_id);1590 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15911592 1593 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15941595 1596 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1597 .checked_sub(1)1598 .ok_or(Error::<T>::NumOverflow)?;1599 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1600 <NftItemList<T>>::remove(collection_id, item_id);16011602 Ok(())1603 }16041605 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1606 ensure!(1607 <FungibleItemList<T>>::contains_key(collection_id, item_id),1608 Error::<T>::TokenNotFound1609 );1610 let item = <FungibleItemList<T>>::get(collection_id, item_id);1611 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16121613 1614 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16151616 1617 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1618 .checked_sub(item.value as u64)1619 .ok_or(Error::<T>::NumOverflow)?;1620 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16211622 <FungibleItemList<T>>::remove(collection_id, item_id);16231624 Ok(())1625 }16261627 fn collection_exists(collection_id: u64) -> DispatchResult {1628 ensure!(1629 <Collection<T>>::contains_key(collection_id),1630 Error::<T>::CollectionNotFound1631 );1632 Ok(())1633 }16341635 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1636 Self::collection_exists(collection_id)?;16371638 let target_collection = <Collection<T>>::get(collection_id);1639 ensure!(1640 subject == target_collection.owner,1641 Error::<T>::NoPermission1642 );16431644 Ok(())1645 }16461647 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1648 let target_collection = <Collection<T>>::get(collection_id);1649 let mut result: bool = subject == target_collection.owner;1650 let exists = <AdminList<T>>::contains_key(collection_id);16511652 if !result & exists {1653 if <AdminList<T>>::get(collection_id).contains(&subject) {1654 result = true1655 }1656 }16571658 result1659 }16601661 fn check_owner_or_admin_permissions(1662 collection_id: u64,1663 subject: T::AccountId,1664 ) -> DispatchResult {1665 Self::collection_exists(collection_id)?;1666 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16671668 ensure!(1669 result,1670 Error::<T>::NoPermission1671 );1672 Ok(())1673 }16741675 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1676 let target_collection = <Collection<T>>::get(collection_id);16771678 match target_collection.mode {1679 CollectionMode::NFT => {1680 <NftItemList<T>>::get(collection_id, item_id).owner == subject1681 }1682 CollectionMode::Fungible(_) => {1683 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1684 }1685 CollectionMode::ReFungible(_) => {1686 <ReFungibleItemList<T>>::get(collection_id, item_id)1687 .owner1688 .iter()1689 .any(|i| i.owner == subject)1690 }1691 CollectionMode::Invalid => false,1692 }1693 }16941695 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1696 let mes = Error::<T>::AddresNotInWhiteList;1697 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1698 let wl = <WhiteList<T>>::get(collection_id);1699 ensure!(wl.contains(address), mes);17001701 Ok(())1702 }17031704 fn transfer_fungible(1705 collection_id: u64,1706 item_id: u64,1707 value: u64,1708 owner: T::AccountId,1709 new_owner: T::AccountId,1710 ) -> DispatchResult {1711 ensure!(1712 <FungibleItemList<T>>::contains_key(collection_id, item_id),1713 Error::<T>::TokenNotFound1714 );17151716 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1717 let amount = full_item.value;17181719 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17201721 1722 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1723 .checked_sub(value)1724 .ok_or(Error::<T>::NumOverflow)?;1725 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17261727 let mut new_owner_account_id = 0;1728 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1729 if new_owner_items.len() > 0 {1730 new_owner_account_id = new_owner_items[0];1731 }17321733 let val64 = value.into();17341735 1736 if amount == val64 && new_owner_account_id == 0 {1737 1738 1739 let mut new_full_item = full_item.clone();1740 new_full_item.owner = new_owner.clone();1741 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17421743 1744 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1745 .checked_add(value)1746 .ok_or(Error::<T>::NumOverflow)?;1747 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17481749 1750 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1751 } else {1752 let mut new_full_item = full_item.clone();1753 new_full_item.value -= val64;17541755 1756 if new_owner_account_id > 0 {1757 1758 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1759 item.value += val64;17601761 1762 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1763 .checked_add(value)1764 .ok_or(Error::<T>::NumOverflow)?;1765 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17661767 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1768 } else {1769 1770 let item = FungibleItemType {1771 collection: collection_id,1772 owner: new_owner.clone(),1773 value: val64,1774 };17751776 Self::add_fungible_item(item)?;1777 }17781779 if amount == val64 {1780 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17811782 1783 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1784 <FungibleItemList<T>>::remove(collection_id, item_id);1785 }17861787 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1788 }17891790 Ok(())1791 }17921793 fn transfer_refungible(1794 collection_id: u64,1795 item_id: u64,1796 value: u64,1797 owner: T::AccountId,1798 new_owner: T::AccountId,1799 ) -> DispatchResult {1800 ensure!(1801 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1802 Error::<T>::TokenNotFound1803 );18041805 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1806 let item = full_item1807 .owner1808 .iter()1809 .filter(|i| i.owner == owner)1810 .next()1811 .ok_or(Error::<T>::NumOverflow)?;1812 let amount = item.fraction;18131814 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18151816 1817 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1818 .checked_sub(value)1819 .ok_or(Error::<T>::NumOverflow)?;1820 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18211822 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1823 .checked_add(value)1824 .ok_or(Error::<T>::NumOverflow)?;1825 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18261827 let old_owner = item.owner.clone();1828 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1829 let val64 = value.into();18301831 1832 if amount == val64 && !new_owner_has_account {1833 1834 1835 let mut new_full_item = full_item.clone();1836 new_full_item1837 .owner1838 .iter_mut()1839 .find(|i| i.owner == owner)1840 .unwrap()1841 .owner = new_owner.clone();1842 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18431844 1845 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1846 } else {1847 let mut new_full_item = full_item.clone();1848 new_full_item1849 .owner1850 .iter_mut()1851 .find(|i| i.owner == owner)1852 .unwrap()1853 .fraction -= val64;18541855 1856 if new_owner_has_account {1857 1858 new_full_item1859 .owner1860 .iter_mut()1861 .find(|i| i.owner == new_owner)1862 .unwrap()1863 .fraction += val64;1864 } else {1865 1866 new_full_item.owner.push(Ownership {1867 owner: new_owner.clone(),1868 fraction: val64,1869 });1870 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1871 }18721873 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1874 }18751876 Ok(())1877 }18781879 fn transfer_nft(1880 collection_id: u64,1881 item_id: u64,1882 sender: T::AccountId,1883 new_owner: T::AccountId,1884 ) -> DispatchResult {1885 ensure!(1886 <NftItemList<T>>::contains_key(collection_id, item_id),1887 Error::<T>::TokenNotFound1888 );18891890 let mut item = <NftItemList<T>>::get(collection_id, item_id);18911892 ensure!(1893 sender == item.owner,1894 Error::<T>::MustBeTokenOwner1895 );18961897 1898 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1899 .checked_sub(1)1900 .ok_or(Error::<T>::NumOverflow)?;1901 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19021903 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1904 .checked_add(1)1905 .ok_or(Error::<T>::NumOverflow)?;1906 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19071908 1909 let old_owner = item.owner.clone();1910 item.owner = new_owner.clone();1911 <NftItemList<T>>::insert(collection_id, item_id, item);19121913 1914 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19151916 1917 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1918 Ok(())1919 }1920 1921 fn item_exists(1922 collection_id: u64,1923 item_id: u64,1924 mode: &CollectionMode1925 ) -> DispatchResult {1926 match mode {1927 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1928 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1929 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1930 _ => ()1931 };1932 1933 Ok(())1934 }19351936 fn set_re_fungible_variable_data(1937 collection_id: u64,1938 item_id: u64,1939 data: Vec<u8>1940 ) -> DispatchResult {1941 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19421943 item.variable_data = data;19441945 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19461947 Ok(())1948 }19491950 fn set_nft_variable_data(1951 collection_id: u64,1952 item_id: u64,1953 data: Vec<u8>1954 ) -> DispatchResult {1955 let mut item = <NftItemList<T>>::get(collection_id, item_id);1956 1957 item.variable_data = data;19581959 <NftItemList<T>>::insert(collection_id, item_id, item);1960 1961 Ok(())1962 }19631964 fn init_collection(item: &CollectionType<T::AccountId>) {1965 1966 assert!(1967 item.decimal_points <= 4,1968 "decimal_points parameter must be lower than 4"1969 );1970 assert!(1971 item.name.len() <= 64,1972 "Collection name can not be longer than 63 char"1973 );1974 assert!(1975 item.name.len() <= 256,1976 "Collection description can not be longer than 255 char"1977 );1978 assert!(1979 item.token_prefix.len() <= 16,1980 "Token prefix can not be longer than 15 char"1981 );19821983 1984 let next_id = CreatedCollectionCount::get()1985 .checked_add(1)1986 .unwrap();19871988 CreatedCollectionCount::put(next_id);1989 }19901991 fn init_nft_token(item: &NftItemType<T::AccountId>) {1992 let current_index = <ItemListIndex>::get(item.collection)1993 .checked_add(1)1994 .unwrap();19951996 let item_owner = item.owner.clone();1997 let collection_id = item.collection.clone();1998 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19992000 <ItemListIndex>::insert(collection_id, current_index);20012002 2003 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2004 .checked_add(1)2005 .unwrap();2006 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2007 }20082009 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2010 let current_index = <ItemListIndex>::get(item.collection)2011 .checked_add(1)2012 .unwrap();2013 let owner = item.owner.clone();2014 let value = item.value as u64;20152016 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20172018 <ItemListIndex>::insert(item.collection, current_index);20192020 2021 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2022 .checked_add(value)2023 .unwrap();2024 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2025 }20262027 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2028 let current_index = <ItemListIndex>::get(item.collection)2029 .checked_add(1)2030 .unwrap();20312032 let value = item.owner.first().unwrap().fraction as u64;2033 let owner = item.owner.first().unwrap().owner.clone();20342035 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20362037 <ItemListIndex>::insert(item.collection, current_index);20382039 2040 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2041 .checked_add(value)2042 .unwrap();2043 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2044 }20452046 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20472048 2049 if <AccountItemCount<T>>::contains_key(owner.clone()) {20502051 2052 let count = <AccountItemCount<T>>::get(owner.clone());2053 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20542055 <AccountItemCount<T>>::insert(owner.clone(), count2056 .checked_add(1)2057 .ok_or(Error::<T>::NumOverflow)?);2058 }2059 else {2060 <AccountItemCount<T>>::insert(owner.clone(), 1);2061 }20622063 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2064 if list_exists {2065 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2066 let item_contains = list.contains(&item_index.clone());20672068 if !item_contains {2069 list.push(item_index.clone());2070 }20712072 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2073 } else {2074 let mut itm = Vec::new();2075 itm.push(item_index.clone());2076 <AddressTokens<T>>::insert(collection_id, owner, itm);2077 2078 }20792080 Ok(())2081 }20822083 fn remove_token_index(2084 collection_id: u64,2085 item_index: u64,2086 owner: T::AccountId,2087 ) -> DispatchResult {20882089 2090 <AccountItemCount<T>>::insert(owner.clone(), 2091 <AccountItemCount<T>>::get(owner.clone())2092 .checked_sub(1)2093 .ok_or(Error::<T>::NumOverflow)?);209420952096 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2097 if list_exists {2098 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2099 let item_contains = list.contains(&item_index.clone());21002101 if item_contains {2102 list.retain(|&item| item != item_index);2103 <AddressTokens<T>>::insert(collection_id, owner, list);2104 }2105 }21062107 Ok(())2108 }21092110 fn move_token_index(2111 collection_id: u64,2112 item_index: u64,2113 old_owner: T::AccountId,2114 new_owner: T::AccountId,2115 ) -> DispatchResult {2116 Self::remove_token_index(collection_id, item_index, old_owner)?;2117 Self::add_token_index(collection_id, item_index, new_owner)?;21182119 Ok(())2120 }2121}2122212321242125212621272128pub type Multiplier = FixedU128;21292130type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2131 <T as system::Trait>::AccountId,2132>>::Balance;2133type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2134 <T as system::Trait>::AccountId,2135>>::NegativeImbalance;2136213721382139#[derive(Encode, Decode, Clone, Eq, PartialEq)]2140pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2141 #[codec(compact)] BalanceOf<T>2142);21432144impl<T: Trait + Send + Sync> sp_std::fmt::Debug2145 for ChargeTransactionPayment<T>2146{2147 #[cfg(feature = "std")]2148 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2149 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2150 }2151 #[cfg(not(feature = "std"))]2152 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2153 Ok(())2154 }2155}21562157impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2158where2159 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2160 BalanceOf<T>: Send + Sync + FixedPointOperand,2161{2162 2163 pub fn from(fee: BalanceOf<T>) -> Self {2164 Self(fee)2165 }21662167 pub fn traditional_fee(2168 len: usize,2169 info: &DispatchInfoOf<T::Call>,2170 tip: BalanceOf<T>,2171 ) -> BalanceOf<T>2172 where2173 T::Call: Dispatchable<Info = DispatchInfo>,2174 {2175 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2176 }21772178 fn withdraw_fee(2179 &self,2180 who: &T::AccountId,2181 call: &T::Call,2182 info: &DispatchInfoOf<T::Call>,2183 len: usize,2184 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2185 let tip = self.0;21862187 2188 2189 2190 2191 2192 2193 2194 let fee = Self::traditional_fee(len, info, tip);21952196 2197 2198 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2199 Some(Call::create_item(collection_id, _properties, _owner)) => {2200 <Collection<T>>::get(collection_id).sponsor2201 }2202 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2203 let _collection_mode = <Collection<T>>::get(collection_id).mode;22042205 2206 let sponsor_transfer = match _collection_mode {2207 CollectionMode::NFT => {2208 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2209 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2210 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2211 if block_number >= limit_time {2212 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2213 true2214 }2215 else {2216 false2217 }2218 }2219 CollectionMode::Fungible(_) => {2220 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2221 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2222 if basket.iter().any(|i| i.address == _new_owner.clone())2223 {2224 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2225 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2226 if block_number >= limit_time {2227 basket.retain(|x| x.address == item.address);2228 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2229 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2230 true2231 }2232 else {2233 false2234 }2235 }2236 else {2237 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2238 true2239 }2240 }2241 CollectionMode::ReFungible(_) => {2242 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2243 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2244 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2245 if block_number >= limit_time {2246 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2247 true2248 } else {2249 false2250 }2251 }2252 _ => {2253 false2254 },2255 };22562257 if !sponsor_transfer {2258 T::AccountId::default()2259 } else {2260 <Collection<T>>::get(collection_id).sponsor2261 }2262 }22632264 _ => T::AccountId::default(),2265 };22662267 2268 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22692270 2271 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22722273 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2274 code_hash,2275 &data,2276 &who,2277 );2278 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22792280 T::AccountId::default()2281 },22822283 2284 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22852286 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22872288 let mut sponsor_transfer = false;2289 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2290 let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2291 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2292 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2293 let limit_time = last_tx_block + rate_limit;22942295 if block_number >= limit_time {2296 <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2297 sponsor_transfer = true;2298 }2299 } else {2300 sponsor_transfer = false;2301 }2302 2303 2304 let mut sp = T::AccountId::default();2305 if sponsor_transfer {2306 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2307 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2308 sp = called_contract;2309 }2310 }2311 }23122313 sp2314 },23152316 _ => sponsor,2317 };23182319 let mut who_pays_fee: T::AccountId = sponsor.clone();2320 if sponsor == T::AccountId::default() {2321 who_pays_fee = who.clone();2322 }23232324 2325 if fee.is_zero() {2326 return Ok((fee, None));2327 }23282329 match <T as transaction_payment::Trait>::Currency::withdraw(2330 &who_pays_fee,2331 fee,2332 if tip.is_zero() {2333 WithdrawReason::TransactionPayment.into()2334 } else {2335 WithdrawReason::TransactionPayment | WithdrawReason::Tip2336 },2337 ExistenceRequirement::KeepAlive,2338 ) {2339 Ok(imbalance) => Ok((fee, Some(imbalance))),2340 Err(_) => Err(InvalidTransaction::Payment.into()),2341 }2342 }2343}234423452346impl<T: Trait + Send + Sync> SignedExtension2347 for ChargeTransactionPayment<T>2348where2349 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2350 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2351{2352 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2353 type AccountId = T::AccountId;2354 type Call = T::Call;2355 type AdditionalSigned = ();2356 type Pre = (2357 BalanceOf<T>,2358 Self::AccountId,2359 Option<NegativeImbalanceOf<T>>,2360 BalanceOf<T>,2361 );2362 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2363 Ok(())2364 }23652366 fn validate(2367 &self,2368 _who: &Self::AccountId,2369 _call: &Self::Call,2370 _info: &DispatchInfoOf<Self::Call>,2371 _len: usize,2372 ) -> TransactionValidity {2373 Ok(ValidTransaction::default())2374 }23752376 fn pre_dispatch(2377 self,2378 who: &Self::AccountId,2379 call: &Self::Call,2380 info: &DispatchInfoOf<Self::Call>,2381 len: usize,2382 ) -> Result<Self::Pre, TransactionValidityError> {2383 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2384 Ok((self.0, who.clone(), imbalance, fee))2385 }23862387 fn post_dispatch(2388 pre: Self::Pre,2389 info: &DispatchInfoOf<Self::Call>,2390 post_info: &PostDispatchInfoOf<Self::Call>,2391 len: usize,2392 _result: &DispatchResult,2393 ) -> Result<(), TransactionValidityError> {2394 let (tip, who, imbalance, fee) = pre;2395 if let Some(payed) = imbalance {2396 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2397 len as u32, info, post_info, tip,2398 );2399 let refund = fee.saturating_sub(actual_fee);2400 let actual_payment =2401 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2402 &who, refund,2403 ) {2404 Ok(refund_imbalance) => {2405 2406 2407 match payed.offset(refund_imbalance) {2408 Ok(actual_payment) => actual_payment,2409 Err(_) => return Err(InvalidTransaction::Payment.into()),2410 }2411 }2412 2413 2414 Err(_) => payed,2415 };2416 let imbalances = actual_payment.split(tip);2417 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2418 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2419 );2420 }2421 Ok(())2422 }2423}24242425