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 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 ApproveNotFound,299 300 TokenValueNotEnough,301 302 ApproveRequired,303 304 AddresNotInWhiteList,305 306 CollectionAdminsLimitExceeded,307 308 AddressOwnershipLimitExceeded,309 310 EmptyArgument,311 312 TokenConstDataLimitExceeded,313 314 TokenVariableDataLimitExceeded,315 316 NotNftDataUsedToMintNftCollectionToken,317 318 NotFungibleDataUsedToMintFungibleCollectionToken,319 320 NotReFungibleDataUsedToMintReFungibleCollectionToken,321 322 UnexpectedCollectionType,323 324 CantStoreMetadataInFungibleTokens325 }326}327328pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {329 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;330331 332 type WeightInfo: WeightInfo;333}334335#[cfg(feature = "runtime-benchmarks")]336mod benchmarking;337338339340decl_storage! {341 trait Store for Module<T: Trait> as Nft {342343 344 NextCollectionID: u64;345 CreatedCollectionCount: u64;346 ChainVersion: u64;347 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;348349 350 pub ChainLimit get(fn chain_limit) config(): ChainLimits;351352 353 CollectionCount: u64;354 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;355356 357 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;358 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;359 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;360361 362 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;363364 365 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;366367 368 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;369 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;370 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;371372 373 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;374375 376 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;377 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>>;378 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;379380 381 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;382 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;383 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;384 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;385 }386 add_extra_genesis {387 build(|config: &GenesisConfig<T>| {388 389 for (_num, _c) in &config.collection {390 <Module<T>>::init_collection(_c);391 }392393 for (_num, _q, _i) in &config.nft_item_id {394 <Module<T>>::init_nft_token(_i);395 }396397 for (_num, _q, _i) in &config.fungible_item_id {398 <Module<T>>::init_fungible_token(_i);399 }400401 for (_num, _q, _i) in &config.refungible_item_id {402 <Module<T>>::init_refungible_token(_i);403 }404 })405 }406}407408decl_event!(409 pub enum Event<T>410 where411 AccountId = <T as system::Trait>::AccountId,412 {413 414 415 416 417 418 419 420 421 422 Created(u64, u8, AccountId),423424 425 426 427 428 429 430 431 ItemCreated(u64, u64),432433 434 435 436 437 438 439 440 ItemDestroyed(u64, u64),441 }442);443444decl_module! {445 pub struct Module<T: Trait> for enum Call where origin: T::Origin {446447 fn deposit_event() = default;448 type Error = Error<T>;449450 fn on_initialize(now: T::BlockNumber) -> Weight {451452 if ChainVersion::get() < 2453 {454 let value = NextCollectionID::get();455 CreatedCollectionCount::put(value);456 ChainVersion::put(2);457 }458459 0460 }461462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 #[weight = T::WeightInfo::create_collection()]479 pub fn create_collection(origin,480 collection_name: Vec<u16>,481 collection_description: Vec<u16>,482 token_prefix: Vec<u8>,483 mode: CollectionMode) -> DispatchResult {484485 486 let who = ensure_signed(origin)?;487488 let decimal_points = match mode {489 CollectionMode::Fungible(points) => points,490 CollectionMode::ReFungible(points) => points,491 _ => 0492 };493494 495 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497 498 ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);499500 let mut name = collection_name.to_vec();501 name.push(0);502 ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503504 let mut description = collection_description.to_vec();505 description.push(0);506 ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);507508 let mut prefix = token_prefix.to_vec();509 prefix.push(0);510 ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);511512 513 let next_id = CreatedCollectionCount::get()514 .checked_add(1)515 .ok_or(Error::<T>::NumOverflow)?;516517 518 let total = CollectionCount::get()519 .checked_add(1)520 .ok_or(Error::<T>::NumOverflow)?;521522 CreatedCollectionCount::put(next_id);523 CollectionCount::put(total);524525 526 let new_collection = CollectionType {527 owner: who.clone(),528 name: name,529 mode: mode.clone(),530 mint_mode: false,531 access: AccessMode::Normal,532 description: description,533 decimal_points: decimal_points,534 token_prefix: prefix,535 offchain_schema: Vec::new(),536 sponsor: T::AccountId::default(),537 unconfirmed_sponsor: T::AccountId::default(),538 variable_on_chain_schema: Vec::new(),539 const_on_chain_schema: Vec::new(),540 };541542 543 <Collection<T>>::insert(next_id, new_collection);544545 546 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));547548 Ok(())549 }550551 552 553 554 555 556 557 558 559 560 #[weight = T::WeightInfo::destroy_collection()]561 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {562563 let sender = ensure_signed(origin)?;564 Self::check_owner_permissions(collection_id, sender)?;565566 <AddressTokens<T>>::remove_prefix(collection_id);567 <ApprovedList<T>>::remove_prefix(collection_id);568 <Balance<T>>::remove_prefix(collection_id);569 <ItemListIndex>::remove(collection_id);570 <AdminList<T>>::remove(collection_id);571 <Collection<T>>::remove(collection_id);572 <WhiteList<T>>::remove(collection_id);573574 <NftItemList<T>>::remove_prefix(collection_id);575 <FungibleItemList<T>>::remove_prefix(collection_id);576 <ReFungibleItemList<T>>::remove_prefix(collection_id);577578 <NftTransferBasket<T>>::remove_prefix(collection_id);579 <FungibleTransferBasket<T>>::remove_prefix(collection_id);580 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);581582 if CollectionCount::get() > 0583 {584 585 let total = CollectionCount::get()586 .checked_sub(1)587 .ok_or(Error::<T>::NumOverflow)?;588589 CollectionCount::put(total);590 }591592 Ok(())593 }594595 596 597 598 599 600 601 602 603 604 605 606 607 #[weight = T::WeightInfo::add_to_white_list()]608 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{609610 let sender = ensure_signed(origin)?;611 Self::check_owner_or_admin_permissions(collection_id, sender)?;612613 let mut white_list_collection: Vec<T::AccountId>;614 if <WhiteList<T>>::contains_key(collection_id) {615 white_list_collection = <WhiteList<T>>::get(collection_id);616 if !white_list_collection.contains(&address.clone())617 {618 white_list_collection.push(address.clone());619 }620 }621 else {622 white_list_collection = Vec::new();623 white_list_collection.push(address.clone());624 }625626 <WhiteList<T>>::insert(collection_id, white_list_collection);627 Ok(())628 }629630 631 632 633 634 635 636 637 638 639 640 641 642 #[weight = T::WeightInfo::remove_from_white_list()]643 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{644645 let sender = ensure_signed(origin)?;646 Self::check_owner_or_admin_permissions(collection_id, sender)?;647648 if <WhiteList<T>>::contains_key(collection_id) {649 let mut white_list_collection = <WhiteList<T>>::get(collection_id);650 if white_list_collection.contains(&address.clone())651 {652 white_list_collection.retain(|i| *i != address.clone());653 <WhiteList<T>>::insert(collection_id, white_list_collection);654 }655 }656657 Ok(())658 }659660 661 662 663 664 665 666 667 668 669 670 671 #[weight = T::WeightInfo::set_public_access_mode()]672 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult673 {674 let sender = ensure_signed(origin)?;675676 Self::check_owner_permissions(collection_id, sender)?;677 let mut target_collection = <Collection<T>>::get(collection_id);678 target_collection.access = mode;679 <Collection<T>>::insert(collection_id, target_collection);680681 Ok(())682 }683684 685 686 687 688 689 690 691 692 693 694 695 696 697 #[weight = T::WeightInfo::set_mint_permission()]698 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult699 {700 let sender = ensure_signed(origin)?;701702 Self::check_owner_permissions(collection_id, sender)?;703 let mut target_collection = <Collection<T>>::get(collection_id);704 target_collection.mint_mode = mint_permission;705 <Collection<T>>::insert(collection_id, target_collection);706707 Ok(())708 }709710 711 712 713 714 715 716 717 718 719 720 721 #[weight = T::WeightInfo::change_collection_owner()]722 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {723724 let sender = ensure_signed(origin)?;725 Self::check_owner_permissions(collection_id, sender)?;726 let mut target_collection = <Collection<T>>::get(collection_id);727 target_collection.owner = new_owner;728 <Collection<T>>::insert(collection_id, target_collection);729730 Ok(())731 }732733 734 735 736 737 738 739 740 741 742 743 744 745 746 #[weight = T::WeightInfo::add_collection_admin()]747 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {748749 let sender = ensure_signed(origin)?;750 Self::check_owner_or_admin_permissions(collection_id, sender)?;751 let mut admin_arr: Vec<T::AccountId> = Vec::new();752753 if <AdminList<T>>::contains_key(collection_id)754 {755 admin_arr = <AdminList<T>>::get(collection_id);756 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);757 }758759 760 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);761762 admin_arr.push(new_admin_id);763 <AdminList<T>>::insert(collection_id, admin_arr);764765 Ok(())766 }767768 769 770 771 772 773 774 775 776 777 778 779 780 #[weight = T::WeightInfo::remove_collection_admin()]781 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {782783 let sender = ensure_signed(origin)?;784 Self::check_owner_or_admin_permissions(collection_id, sender)?;785786 if <AdminList<T>>::contains_key(collection_id)787 {788 let mut admin_arr = <AdminList<T>>::get(collection_id);789 admin_arr.retain(|i| *i != account_id);790 <AdminList<T>>::insert(collection_id, admin_arr);791 }792793 Ok(())794 }795796 797 798 799 800 801 802 803 804 805 #[weight = T::WeightInfo::set_collection_sponsor()]806 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {807808 let sender = ensure_signed(origin)?;809 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);810811 let mut target_collection = <Collection<T>>::get(collection_id);812 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);813814 target_collection.unconfirmed_sponsor = new_sponsor;815 <Collection<T>>::insert(collection_id, target_collection);816817 Ok(())818 }819820 821 822 823 824 825 826 827 #[weight = T::WeightInfo::confirm_sponsorship()]828 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {829830 let sender = ensure_signed(origin)?;831 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833 let mut target_collection = <Collection<T>>::get(collection_id);834 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);835836 target_collection.sponsor = target_collection.unconfirmed_sponsor;837 target_collection.unconfirmed_sponsor = T::AccountId::default();838 <Collection<T>>::insert(collection_id, target_collection);839840 Ok(())841 }842843 844 845 846 847 848 849 850 851 852 #[weight = T::WeightInfo::remove_collection_sponsor()]853 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {854855 let sender = ensure_signed(origin)?;856 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);857858 let mut target_collection = <Collection<T>>::get(collection_id);859 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);860861 target_collection.sponsor = T::AccountId::default();862 <Collection<T>>::insert(collection_id, target_collection);863864 Ok(())865 }866867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890891 #[weight = T::WeightInfo::create_item(data.len())]892 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {893894 let sender = ensure_signed(origin)?;895896 Self::collection_exists(collection_id)?;897898 let target_collection = <Collection<T>>::get(collection_id);899900 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;901 Self::validate_create_item_args(&target_collection, &data)?;902 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;903904 Ok(())905 }906907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 #[weight = T::WeightInfo::create_item(items_data.into_iter()926 .map(|data| { data.len() })927 .sum())]928 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {929930 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);931 let sender = ensure_signed(origin)?;932933 Self::collection_exists(collection_id)?;934 let target_collection = <Collection<T>>::get(collection_id);935936 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;937938 for data in &items_data {939 Self::validate_create_item_args(&target_collection, data)?;940 }941 for data in &items_data {942 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;943 }944945 Ok(())946 }947948 949 950 951 952 953 954 955 956 957 958 959 960 961 #[weight = T::WeightInfo::burn_item()]962 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {963964 let sender = ensure_signed(origin)?;965 Self::collection_exists(collection_id)?;966967 968 let target_collection = <Collection<T>>::get(collection_id);969 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||970 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),971 Error::<T>::NoPermission);972973 if target_collection.access == AccessMode::WhiteList {974 Self::check_white_list(collection_id, &sender)?;975 }976977 match target_collection.mode978 {979 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,980 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,981 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,982 _ => ()983 };984985 986 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));987988 Ok(())989 }990991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 #[weight = T::WeightInfo::transfer()]1015 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10161017 let sender = ensure_signed(origin)?;10181019 1020 let target_collection = <Collection<T>>::get(collection_id);1021 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1022 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023 Error::<T>::NoPermission);10241025 if target_collection.access == AccessMode::WhiteList {1026 Self::check_white_list(collection_id, &sender)?;1027 Self::check_white_list(collection_id, &recipient)?;1028 }10291030 match target_collection.mode1031 {1032 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1033 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1034 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1035 _ => ()1036 };10371038 Ok(())1039 }10401041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 #[weight = T::WeightInfo::approve()]1057 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10581059 let sender = ensure_signed(origin)?;10601061 1062 let target_collection = <Collection<T>>::get(collection_id);1063 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065 Error::<T>::NoPermission);10661067 if target_collection.access == AccessMode::WhiteList {1068 Self::check_white_list(collection_id, &sender)?;1069 Self::check_white_list(collection_id, &approved)?;1070 }10711072 1073 let amount = 100000000;10741075 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1076 if list_exists {10771078 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1079 let item_contains = list.iter().any(|i| i.approved == approved);10801081 if !item_contains {1082 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1083 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1084 }1085 } else {10861087 let mut list = Vec::new();1088 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1089 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1090 }10911092 Ok(())1093 }1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 #[weight = T::WeightInfo::transfer_from()]1115 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11161117 let sender = ensure_signed(origin)?;1118 let mut appoved_transfer = false;11191120 1121 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1122 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1123 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1124 if opt_item.is_some()1125 {1126 appoved_transfer = true;1127 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1128 }1129 }11301131 1132 let target_collection = <Collection<T>>::get(collection_id);1133 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1134 Error::<T>::NoPermission);11351136 if target_collection.access == AccessMode::WhiteList {1137 Self::check_white_list(collection_id, &sender)?;1138 Self::check_white_list(collection_id, &recipient)?;1139 }11401141 1142 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1143 .into_iter().filter(|i| i.approved != sender.clone()).collect();1144 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114511461147 match target_collection.mode1148 {1149 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1150 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1151 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1152 _ => ()1153 };11541155 Ok(())1156 }11571158 1159 #[weight = 0]1160 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11611162 1163 1164 1165 11661167 11681169 11701171 Ok(())1172 }11731174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 #[weight = T::WeightInfo::set_variable_meta_data()]1187 pub fn set_variable_meta_data (1188 origin,1189 collection_id: u64,1190 item_id: u64,1191 data: Vec<u8>1192 ) -> DispatchResult {1193 let sender = ensure_signed(origin)?;1194 1195 Self::collection_exists(collection_id)?;1196 1197 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11981199 1200 let target_collection = <Collection<T>>::get(collection_id);1201 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1202 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1203 Error::<T>::NoPermission);12041205 Self::item_exists(collection_id, item_id, &target_collection.mode)?;12061207 match target_collection.mode1208 {1209 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1210 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1211 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1212 _ => fail!(Error::<T>::UnexpectedCollectionType)1213 };12141215 Ok(())1216 }1217 12181219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 #[weight = T::WeightInfo::set_offchain_schema()]1232 pub fn set_offchain_schema(1233 origin,1234 collection_id: u64,1235 schema: Vec<u8>1236 ) -> DispatchResult {1237 let sender = ensure_signed(origin)?;1238 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12391240 let mut target_collection = <Collection<T>>::get(collection_id);1241 target_collection.offchain_schema = schema;1242 <Collection<T>>::insert(collection_id, target_collection);12431244 Ok(())1245 }12461247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 #[weight = T::WeightInfo::set_const_on_chain_schema()]1260 pub fn set_const_on_chain_schema (1261 origin,1262 collection_id: u64,1263 schema: Vec<u8>1264 ) -> DispatchResult {1265 let sender = ensure_signed(origin)?;1266 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12671268 let mut target_collection = <Collection<T>>::get(collection_id);1269 target_collection.const_on_chain_schema = schema;1270 <Collection<T>>::insert(collection_id, target_collection);12711272 Ok(())1273 }12741275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 #[weight = T::WeightInfo::set_const_on_chain_schema()]1288 pub fn set_variable_on_chain_schema (1289 origin,1290 collection_id: u64,1291 schema: Vec<u8>1292 ) -> DispatchResult {1293 let sender = ensure_signed(origin)?;1294 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12951296 let mut target_collection = <Collection<T>>::get(collection_id);1297 target_collection.variable_on_chain_schema = schema;1298 <Collection<T>>::insert(collection_id, target_collection);12991300 Ok(())1301 }13021303 1304 #[weight = 0]1305 pub fn set_chain_limits(1306 origin,1307 limits: ChainLimits1308 ) -> DispatchResult {1309 ensure_root(origin)?;1310 <ChainLimit>::put(limits);1311 Ok(())1312 }13131314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 #[weight = 0]1326 pub fn enable_contract_sponsoring(1327 origin,1328 contract_address: T::AccountId,1329 enable: bool1330 ) -> DispatchResult {1331 let sender = ensure_signed(origin)?;1332 let mut is_owner = false;1333 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1334 let owner = <ContractOwner<T>>::get(&contract_address);1335 is_owner = sender == owner;1336 }1337 ensure!(is_owner, Error::<T>::NoPermission);13381339 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1340 Ok(())1341 }13421343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 #[weight = 0]1361 pub fn set_contract_sponsoring_rate_limit(1362 origin,1363 contract_address: T::AccountId,1364 rate_limit: T::BlockNumber1365 ) -> DispatchResult {1366 let sender = ensure_signed(origin)?;1367 let mut is_owner = false;1368 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1369 let owner = <ContractOwner<T>>::get(&contract_address);1370 is_owner = sender == owner;1371 }1372 ensure!(is_owner, Error::<T>::NoPermission);13731374 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1375 Ok(())1376 }13771378 }1379}13801381impl<T: Trait> Module<T> {13821383 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13841385 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1386 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1387 Self::check_white_list(collection_id, owner)?;1388 Self::check_white_list(collection_id, sender)?;1389 }13901391 Ok(())1392 }13931394 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1395 match target_collection.mode1396 {1397 CollectionMode::NFT => {1398 if let CreateItemData::NFT(data) = data {1399 1400 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1401 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1402 } else {1403 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1404 }1405 },1406 CollectionMode::Fungible(_) => {1407 if let CreateItemData::Fungible(_) = data {1408 } else {1409 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1410 }1411 },1412 CollectionMode::ReFungible(_) => {1413 if let CreateItemData::ReFungible(data) = data {14141415 1416 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1417 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1418 } else {1419 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1420 }1421 },1422 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1423 };14241425 Ok(())1426 }14271428 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1429 match data1430 {1431 CreateItemData::NFT(data) => {1432 let item = NftItemType {1433 collection: collection_id,1434 owner,1435 const_data: data.const_data,1436 variable_data: data.variable_data1437 };14381439 Self::add_nft_item(item)?;1440 },1441 CreateItemData::Fungible(_) => {1442 let item = FungibleItemType {1443 collection: collection_id,1444 owner,1445 value: (10 as u128).pow(collection.decimal_points)1446 };14471448 Self::add_fungible_item(item)?;1449 },1450 CreateItemData::ReFungible(data) => {1451 let mut owner_list = Vec::new();1452 let value = (10 as u128).pow(collection.decimal_points);1453 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14541455 let item = ReFungibleItemType {1456 collection: collection_id,1457 owner: owner_list,1458 const_data: data.const_data,1459 variable_data: data.variable_data1460 };14611462 Self::add_refungible_item(item)?;1463 }1464 };146514661467 1468 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14691470 Ok(())1471 }14721473 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1474 let current_index = <ItemListIndex>::get(item.collection)1475 .checked_add(1)1476 .ok_or(Error::<T>::NumOverflow)?;1477 let itemcopy = item.clone();1478 let owner = item.owner.clone();1479 let value = item.value as u64;14801481 Self::add_token_index(item.collection, current_index, owner.clone())?;14821483 <ItemListIndex>::insert(item.collection, current_index);1484 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14851486 1487 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1488 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1489 1490 1491 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1492 .checked_add(value)1493 .ok_or(Error::<T>::NumOverflow)?;1494 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14951496 Ok(())1497 }14981499 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1500 let current_index = <ItemListIndex>::get(item.collection)1501 .checked_add(1)1502 .ok_or(Error::<T>::NumOverflow)?;1503 let itemcopy = item.clone();15041505 let value = item.owner.first().unwrap().fraction as u64;1506 let owner = item.owner.first().unwrap().owner.clone();15071508 Self::add_token_index(item.collection, current_index, owner.clone())?;15091510 <ItemListIndex>::insert(item.collection, current_index);1511 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15121513 1514 let block_number: T::BlockNumber = 0.into();1515 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15161517 1518 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1519 .checked_add(value)1520 .ok_or(Error::<T>::NumOverflow)?;1521 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15221523 Ok(())1524 }15251526 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1527 let current_index = <ItemListIndex>::get(item.collection)1528 .checked_add(1)1529 .ok_or(Error::<T>::NumOverflow)?;15301531 let item_owner = item.owner.clone();1532 let collection_id = item.collection.clone();1533 Self::add_token_index(collection_id, current_index, item.owner.clone())?;15341535 <ItemListIndex>::insert(collection_id, current_index);1536 <NftItemList<T>>::insert(collection_id, current_index, item);15371538 1539 let block_number: T::BlockNumber = 0.into();1540 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15411542 1543 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1544 .checked_add(1)1545 .ok_or(Error::<T>::NumOverflow)?;1546 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15471548 Ok(())1549 }15501551 fn burn_refungible_item(1552 collection_id: u64,1553 item_id: u64,1554 owner: T::AccountId,1555 ) -> DispatchResult {1556 ensure!(1557 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1558 Error::<T>::TokenNotFound1559 );1560 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1561 let item = collection1562 .owner1563 .iter()1564 .filter(|&i| i.owner == owner)1565 .next()1566 .unwrap();1567 Self::remove_token_index(collection_id, item_id, owner.clone())?;15681569 1570 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15711572 1573 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1574 .checked_sub(item.fraction as u64)1575 .ok_or(Error::<T>::NumOverflow)?;1576 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15771578 <ReFungibleItemList<T>>::remove(collection_id, item_id);15791580 Ok(())1581 }15821583 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1584 ensure!(1585 <NftItemList<T>>::contains_key(collection_id, item_id),1586 Error::<T>::TokenNotFound1587 );1588 let item = <NftItemList<T>>::get(collection_id, item_id);1589 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15901591 1592 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15931594 1595 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1596 .checked_sub(1)1597 .ok_or(Error::<T>::NumOverflow)?;1598 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1599 <NftItemList<T>>::remove(collection_id, item_id);16001601 Ok(())1602 }16031604 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1605 ensure!(1606 <FungibleItemList<T>>::contains_key(collection_id, item_id),1607 Error::<T>::TokenNotFound1608 );1609 let item = <FungibleItemList<T>>::get(collection_id, item_id);1610 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16111612 1613 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16141615 1616 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1617 .checked_sub(item.value as u64)1618 .ok_or(Error::<T>::NumOverflow)?;1619 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16201621 <FungibleItemList<T>>::remove(collection_id, item_id);16221623 Ok(())1624 }16251626 fn collection_exists(collection_id: u64) -> DispatchResult {1627 ensure!(1628 <Collection<T>>::contains_key(collection_id),1629 Error::<T>::CollectionNotFound1630 );1631 Ok(())1632 }16331634 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1635 Self::collection_exists(collection_id)?;16361637 let target_collection = <Collection<T>>::get(collection_id);1638 ensure!(1639 subject == target_collection.owner,1640 Error::<T>::NoPermission1641 );16421643 Ok(())1644 }16451646 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1647 let target_collection = <Collection<T>>::get(collection_id);1648 let mut result: bool = subject == target_collection.owner;1649 let exists = <AdminList<T>>::contains_key(collection_id);16501651 if !result & exists {1652 if <AdminList<T>>::get(collection_id).contains(&subject) {1653 result = true1654 }1655 }16561657 result1658 }16591660 fn check_owner_or_admin_permissions(1661 collection_id: u64,1662 subject: T::AccountId,1663 ) -> DispatchResult {1664 Self::collection_exists(collection_id)?;1665 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16661667 ensure!(1668 result,1669 Error::<T>::NoPermission1670 );1671 Ok(())1672 }16731674 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1675 let target_collection = <Collection<T>>::get(collection_id);16761677 match target_collection.mode {1678 CollectionMode::NFT => {1679 <NftItemList<T>>::get(collection_id, item_id).owner == subject1680 }1681 CollectionMode::Fungible(_) => {1682 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1683 }1684 CollectionMode::ReFungible(_) => {1685 <ReFungibleItemList<T>>::get(collection_id, item_id)1686 .owner1687 .iter()1688 .any(|i| i.owner == subject)1689 }1690 CollectionMode::Invalid => false,1691 }1692 }16931694 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1695 let mes = Error::<T>::AddresNotInWhiteList;1696 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1697 let wl = <WhiteList<T>>::get(collection_id);1698 ensure!(wl.contains(address), mes);16991700 Ok(())1701 }17021703 fn transfer_fungible(1704 collection_id: u64,1705 item_id: u64,1706 value: u64,1707 owner: T::AccountId,1708 new_owner: T::AccountId,1709 ) -> DispatchResult {1710 ensure!(1711 <FungibleItemList<T>>::contains_key(collection_id, item_id),1712 Error::<T>::TokenNotFound1713 );17141715 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1716 let amount = full_item.value;17171718 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17191720 1721 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1722 .checked_sub(value)1723 .ok_or(Error::<T>::NumOverflow)?;1724 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17251726 let mut new_owner_account_id = 0;1727 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1728 if new_owner_items.len() > 0 {1729 new_owner_account_id = new_owner_items[0];1730 }17311732 let val64 = value.into();17331734 1735 if amount == val64 && new_owner_account_id == 0 {1736 1737 1738 let mut new_full_item = full_item.clone();1739 new_full_item.owner = new_owner.clone();1740 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17411742 1743 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1744 .checked_add(value)1745 .ok_or(Error::<T>::NumOverflow)?;1746 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17471748 1749 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1750 } else {1751 let mut new_full_item = full_item.clone();1752 new_full_item.value -= val64;17531754 1755 if new_owner_account_id > 0 {1756 1757 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1758 item.value += val64;17591760 1761 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1762 .checked_add(value)1763 .ok_or(Error::<T>::NumOverflow)?;1764 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17651766 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1767 } else {1768 1769 let item = FungibleItemType {1770 collection: collection_id,1771 owner: new_owner.clone(),1772 value: val64,1773 };17741775 Self::add_fungible_item(item)?;1776 }17771778 if amount == val64 {1779 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17801781 1782 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1783 <FungibleItemList<T>>::remove(collection_id, item_id);1784 }17851786 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1787 }17881789 Ok(())1790 }17911792 fn transfer_refungible(1793 collection_id: u64,1794 item_id: u64,1795 value: u64,1796 owner: T::AccountId,1797 new_owner: T::AccountId,1798 ) -> DispatchResult {1799 ensure!(1800 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1801 Error::<T>::TokenNotFound1802 );18031804 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1805 let item = full_item1806 .owner1807 .iter()1808 .filter(|i| i.owner == owner)1809 .next()1810 .ok_or(Error::<T>::NumOverflow)?;1811 let amount = item.fraction;18121813 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18141815 1816 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1817 .checked_sub(value)1818 .ok_or(Error::<T>::NumOverflow)?;1819 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18201821 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1822 .checked_add(value)1823 .ok_or(Error::<T>::NumOverflow)?;1824 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18251826 let old_owner = item.owner.clone();1827 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1828 let val64 = value.into();18291830 1831 if amount == val64 && !new_owner_has_account {1832 1833 1834 let mut new_full_item = full_item.clone();1835 new_full_item1836 .owner1837 .iter_mut()1838 .find(|i| i.owner == owner)1839 .unwrap()1840 .owner = new_owner.clone();1841 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18421843 1844 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1845 } else {1846 let mut new_full_item = full_item.clone();1847 new_full_item1848 .owner1849 .iter_mut()1850 .find(|i| i.owner == owner)1851 .unwrap()1852 .fraction -= val64;18531854 1855 if new_owner_has_account {1856 1857 new_full_item1858 .owner1859 .iter_mut()1860 .find(|i| i.owner == new_owner)1861 .unwrap()1862 .fraction += val64;1863 } else {1864 1865 new_full_item.owner.push(Ownership {1866 owner: new_owner.clone(),1867 fraction: val64,1868 });1869 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1870 }18711872 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1873 }18741875 Ok(())1876 }18771878 fn transfer_nft(1879 collection_id: u64,1880 item_id: u64,1881 sender: T::AccountId,1882 new_owner: T::AccountId,1883 ) -> DispatchResult {1884 ensure!(1885 <NftItemList<T>>::contains_key(collection_id, item_id),1886 Error::<T>::TokenNotFound1887 );18881889 let mut item = <NftItemList<T>>::get(collection_id, item_id);18901891 ensure!(1892 sender == item.owner,1893 Error::<T>::MustBeTokenOwner1894 );18951896 1897 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1898 .checked_sub(1)1899 .ok_or(Error::<T>::NumOverflow)?;1900 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19011902 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1903 .checked_add(1)1904 .ok_or(Error::<T>::NumOverflow)?;1905 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19061907 1908 let old_owner = item.owner.clone();1909 item.owner = new_owner.clone();1910 <NftItemList<T>>::insert(collection_id, item_id, item);19111912 1913 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19141915 1916 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1917 Ok(())1918 }1919 1920 fn item_exists(1921 collection_id: u64,1922 item_id: u64,1923 mode: &CollectionMode1924 ) -> DispatchResult {1925 match mode {1926 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1927 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1928 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1929 _ => ()1930 };1931 1932 Ok(())1933 }19341935 fn set_re_fungible_variable_data(1936 collection_id: u64,1937 item_id: u64,1938 data: Vec<u8>1939 ) -> DispatchResult {1940 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19411942 item.variable_data = data;19431944 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19451946 Ok(())1947 }19481949 fn set_nft_variable_data(1950 collection_id: u64,1951 item_id: u64,1952 data: Vec<u8>1953 ) -> DispatchResult {1954 let mut item = <NftItemList<T>>::get(collection_id, item_id);1955 1956 item.variable_data = data;19571958 <NftItemList<T>>::insert(collection_id, item_id, item);1959 1960 Ok(())1961 }19621963 fn init_collection(item: &CollectionType<T::AccountId>) {1964 1965 assert!(1966 item.decimal_points <= 4,1967 "decimal_points parameter must be lower than 4"1968 );1969 assert!(1970 item.name.len() <= 64,1971 "Collection name can not be longer than 63 char"1972 );1973 assert!(1974 item.name.len() <= 256,1975 "Collection description can not be longer than 255 char"1976 );1977 assert!(1978 item.token_prefix.len() <= 16,1979 "Token prefix can not be longer than 15 char"1980 );19811982 1983 let next_id = CreatedCollectionCount::get()1984 .checked_add(1)1985 .unwrap();19861987 CreatedCollectionCount::put(next_id);1988 }19891990 fn init_nft_token(item: &NftItemType<T::AccountId>) {1991 let current_index = <ItemListIndex>::get(item.collection)1992 .checked_add(1)1993 .unwrap();19941995 let item_owner = item.owner.clone();1996 let collection_id = item.collection.clone();1997 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19981999 <ItemListIndex>::insert(collection_id, current_index);20002001 2002 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2003 .checked_add(1)2004 .unwrap();2005 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2006 }20072008 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2009 let current_index = <ItemListIndex>::get(item.collection)2010 .checked_add(1)2011 .unwrap();2012 let owner = item.owner.clone();2013 let value = item.value as u64;20142015 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20162017 <ItemListIndex>::insert(item.collection, current_index);20182019 2020 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2021 .checked_add(value)2022 .unwrap();2023 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2024 }20252026 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2027 let current_index = <ItemListIndex>::get(item.collection)2028 .checked_add(1)2029 .unwrap();20302031 let value = item.owner.first().unwrap().fraction as u64;2032 let owner = item.owner.first().unwrap().owner.clone();20332034 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20352036 <ItemListIndex>::insert(item.collection, current_index);20372038 2039 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2040 .checked_add(value)2041 .unwrap();2042 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2043 }20442045 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20462047 2048 if <AccountItemCount<T>>::contains_key(owner.clone()) {20492050 2051 let count = <AccountItemCount<T>>::get(owner.clone());2052 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20532054 <AccountItemCount<T>>::insert(owner.clone(), count2055 .checked_add(1)2056 .ok_or(Error::<T>::NumOverflow)?);2057 }2058 else {2059 <AccountItemCount<T>>::insert(owner.clone(), 1);2060 }20612062 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2063 if list_exists {2064 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2065 let item_contains = list.contains(&item_index.clone());20662067 if !item_contains {2068 list.push(item_index.clone());2069 }20702071 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2072 } else {2073 let mut itm = Vec::new();2074 itm.push(item_index.clone());2075 <AddressTokens<T>>::insert(collection_id, owner, itm);2076 2077 }20782079 Ok(())2080 }20812082 fn remove_token_index(2083 collection_id: u64,2084 item_index: u64,2085 owner: T::AccountId,2086 ) -> DispatchResult {20872088 2089 <AccountItemCount<T>>::insert(owner.clone(), 2090 <AccountItemCount<T>>::get(owner.clone())2091 .checked_sub(1)2092 .ok_or(Error::<T>::NumOverflow)?);209320942095 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2096 if list_exists {2097 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2098 let item_contains = list.contains(&item_index.clone());20992100 if item_contains {2101 list.retain(|&item| item != item_index);2102 <AddressTokens<T>>::insert(collection_id, owner, list);2103 }2104 }21052106 Ok(())2107 }21082109 fn move_token_index(2110 collection_id: u64,2111 item_index: u64,2112 old_owner: T::AccountId,2113 new_owner: T::AccountId,2114 ) -> DispatchResult {2115 Self::remove_token_index(collection_id, item_index, old_owner)?;2116 Self::add_token_index(collection_id, item_index, new_owner)?;21172118 Ok(())2119 }2120}2121212221232124212521262127pub type Multiplier = FixedU128;21282129type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2130 <T as system::Trait>::AccountId,2131>>::Balance;2132type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2133 <T as system::Trait>::AccountId,2134>>::NegativeImbalance;2135213621372138#[derive(Encode, Decode, Clone, Eq, PartialEq)]2139pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2140 #[codec(compact)] BalanceOf<T>2141);21422143impl<T: Trait + Send + Sync> sp_std::fmt::Debug2144 for ChargeTransactionPayment<T>2145{2146 #[cfg(feature = "std")]2147 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2148 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2149 }2150 #[cfg(not(feature = "std"))]2151 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2152 Ok(())2153 }2154}21552156impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2157where2158 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2159 BalanceOf<T>: Send + Sync + FixedPointOperand,2160{2161 2162 pub fn from(fee: BalanceOf<T>) -> Self {2163 Self(fee)2164 }21652166 pub fn traditional_fee(2167 len: usize,2168 info: &DispatchInfoOf<T::Call>,2169 tip: BalanceOf<T>,2170 ) -> BalanceOf<T>2171 where2172 T::Call: Dispatchable<Info = DispatchInfo>,2173 {2174 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2175 }21762177 fn withdraw_fee(2178 &self,2179 who: &T::AccountId,2180 call: &T::Call,2181 info: &DispatchInfoOf<T::Call>,2182 len: usize,2183 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2184 let tip = self.0;21852186 2187 2188 2189 2190 2191 2192 2193 let fee = Self::traditional_fee(len, info, tip);21942195 2196 2197 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2198 Some(Call::create_item(collection_id, _properties, _owner)) => {2199 <Collection<T>>::get(collection_id).sponsor2200 }2201 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2202 let _collection_mode = <Collection<T>>::get(collection_id).mode;22032204 2205 let sponsor_transfer = match _collection_mode {2206 CollectionMode::NFT => {2207 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2208 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2209 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2210 if block_number >= limit_time {2211 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2212 true2213 }2214 else {2215 false2216 }2217 }2218 CollectionMode::Fungible(_) => {2219 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2220 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2221 if basket.iter().any(|i| i.address == _new_owner.clone())2222 {2223 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2224 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2225 if block_number >= limit_time {2226 basket.retain(|x| x.address == item.address);2227 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2228 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2229 true2230 }2231 else {2232 false2233 }2234 }2235 else {2236 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2237 true2238 }2239 }2240 CollectionMode::ReFungible(_) => {2241 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2242 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2243 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2244 if block_number >= limit_time {2245 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2246 true2247 } else {2248 false2249 }2250 }2251 _ => {2252 false2253 },2254 };22552256 if !sponsor_transfer {2257 T::AccountId::default()2258 } else {2259 <Collection<T>>::get(collection_id).sponsor2260 }2261 }22622263 _ => T::AccountId::default(),2264 };22652266 2267 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22682269 2270 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22712272 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2273 code_hash,2274 &data,2275 &who,2276 );2277 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22782279 T::AccountId::default()2280 },22812282 2283 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22842285 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22862287 let mut sponsor_transfer = false;2288 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2289 let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2290 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2291 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2292 let limit_time = last_tx_block + rate_limit;22932294 if block_number >= limit_time {2295 <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2296 sponsor_transfer = true;2297 }2298 } else {2299 sponsor_transfer = false;2300 }2301 2302 2303 let mut sp = T::AccountId::default();2304 if sponsor_transfer {2305 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2306 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2307 sp = called_contract;2308 }2309 }2310 }23112312 sp2313 },23142315 _ => sponsor,2316 };23172318 let mut who_pays_fee: T::AccountId = sponsor.clone();2319 if sponsor == T::AccountId::default() {2320 who_pays_fee = who.clone();2321 }23222323 2324 if fee.is_zero() {2325 return Ok((fee, None));2326 }23272328 match <T as transaction_payment::Trait>::Currency::withdraw(2329 &who_pays_fee,2330 fee,2331 if tip.is_zero() {2332 WithdrawReason::TransactionPayment.into()2333 } else {2334 WithdrawReason::TransactionPayment | WithdrawReason::Tip2335 },2336 ExistenceRequirement::KeepAlive,2337 ) {2338 Ok(imbalance) => Ok((fee, Some(imbalance))),2339 Err(_) => Err(InvalidTransaction::Payment.into()),2340 }2341 }2342}234323442345impl<T: Trait + Send + Sync> SignedExtension2346 for ChargeTransactionPayment<T>2347where2348 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2349 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2350{2351 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2352 type AccountId = T::AccountId;2353 type Call = T::Call;2354 type AdditionalSigned = ();2355 type Pre = (2356 BalanceOf<T>,2357 Self::AccountId,2358 Option<NegativeImbalanceOf<T>>,2359 BalanceOf<T>,2360 );2361 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2362 Ok(())2363 }23642365 fn validate(2366 &self,2367 _who: &Self::AccountId,2368 _call: &Self::Call,2369 _info: &DispatchInfoOf<Self::Call>,2370 _len: usize,2371 ) -> TransactionValidity {2372 Ok(ValidTransaction::default())2373 }23742375 fn pre_dispatch(2376 self,2377 who: &Self::AccountId,2378 call: &Self::Call,2379 info: &DispatchInfoOf<Self::Call>,2380 len: usize,2381 ) -> Result<Self::Pre, TransactionValidityError> {2382 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2383 Ok((self.0, who.clone(), imbalance, fee))2384 }23852386 fn post_dispatch(2387 pre: Self::Pre,2388 info: &DispatchInfoOf<Self::Call>,2389 post_info: &PostDispatchInfoOf<Self::Call>,2390 len: usize,2391 _result: &DispatchResult,2392 ) -> Result<(), TransactionValidityError> {2393 let (tip, who, imbalance, fee) = pre;2394 if let Some(payed) = imbalance {2395 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2396 len as u32, info, post_info, tip,2397 );2398 let refund = fee.saturating_sub(actual_fee);2399 let actual_payment =2400 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2401 &who, refund,2402 ) {2403 Ok(refund_imbalance) => {2404 2405 2406 match payed.offset(refund_imbalance) {2407 Ok(actual_payment) => actual_payment,2408 Err(_) => return Err(InvalidTransaction::Payment.into()),2409 }2410 }2411 2412 2413 Err(_) => payed,2414 };2415 let imbalances = actual_payment.split(tip);2416 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2417 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2418 );2419 }2420 Ok(())2421 }2422}24232424