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 CantSotreMetadataInFungibleTokens325 }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)?;11961197 1198 let target_collection = <Collection<T>>::get(collection_id);1199 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1200 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1201 Error::<T>::NoPermission);12021203 Self::item_exists(collection_id, item_id, &target_collection.mode)?;12041205 match target_collection.mode1206 {1207 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1208 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1209 CollectionMode::Fungible(_) => fail!(Error::<T>::CantSotreMetadataInFungibleTokens),1210 _ => fail!(Error::<T>::UnexpectedCollectionType)1211 };12121213 Ok(())1214 }1215 12161217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 #[weight = T::WeightInfo::set_offchain_schema()]1230 pub fn set_offchain_schema(1231 origin,1232 collection_id: u64,1233 schema: Vec<u8>1234 ) -> DispatchResult {1235 let sender = ensure_signed(origin)?;1236 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12371238 let mut target_collection = <Collection<T>>::get(collection_id);1239 target_collection.offchain_schema = schema;1240 <Collection<T>>::insert(collection_id, target_collection);12411242 Ok(())1243 }12441245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 #[weight = T::WeightInfo::set_const_on_chain_schema()]1258 pub fn set_const_on_chain_schema (1259 origin,1260 collection_id: u64,1261 schema: Vec<u8>1262 ) -> DispatchResult {1263 let sender = ensure_signed(origin)?;1264 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12651266 let mut target_collection = <Collection<T>>::get(collection_id);1267 target_collection.const_on_chain_schema = schema;1268 <Collection<T>>::insert(collection_id, target_collection);12691270 Ok(())1271 }12721273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 #[weight = T::WeightInfo::set_const_on_chain_schema()]1286 pub fn set_variable_on_chain_schema (1287 origin,1288 collection_id: u64,1289 schema: Vec<u8>1290 ) -> DispatchResult {1291 let sender = ensure_signed(origin)?;1292 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12931294 let mut target_collection = <Collection<T>>::get(collection_id);1295 target_collection.variable_on_chain_schema = schema;1296 <Collection<T>>::insert(collection_id, target_collection);12971298 Ok(())1299 }13001301 1302 #[weight = 0]1303 pub fn set_chain_limits(1304 origin,1305 limits: ChainLimits1306 ) -> DispatchResult {1307 ensure_root(origin)?;1308 <ChainLimit>::put(limits);1309 Ok(())1310 }13111312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 #[weight = 0]1324 pub fn enable_contract_sponsoring(1325 origin,1326 contract_address: T::AccountId,1327 enable: bool1328 ) -> DispatchResult {1329 let sender = ensure_signed(origin)?;1330 let mut is_owner = false;1331 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1332 let owner = <ContractOwner<T>>::get(&contract_address);1333 is_owner = sender == owner;1334 }1335 ensure!(is_owner, Error::<T>::NoPermission);13361337 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1338 Ok(())1339 }13401341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 #[weight = 0]1359 pub fn set_contract_sponsoring_rate_limit(1360 origin,1361 contract_address: T::AccountId,1362 rate_limit: T::BlockNumber1363 ) -> DispatchResult {1364 let sender = ensure_signed(origin)?;1365 let mut is_owner = false;1366 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1367 let owner = <ContractOwner<T>>::get(&contract_address);1368 is_owner = sender == owner;1369 }1370 ensure!(is_owner, Error::<T>::NoPermission);13711372 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1373 Ok(())1374 }13751376 }1377}13781379impl<T: Trait> Module<T> {13801381 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13821383 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1384 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1385 Self::check_white_list(collection_id, owner)?;1386 Self::check_white_list(collection_id, sender)?;1387 }13881389 Ok(())1390 }13911392 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1393 match target_collection.mode1394 {1395 CollectionMode::NFT => {1396 if let CreateItemData::NFT(data) = data {1397 1398 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1399 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1400 } else {1401 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1402 }1403 },1404 CollectionMode::Fungible(_) => {1405 if let CreateItemData::Fungible(_) = data {1406 } else {1407 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1408 }1409 },1410 CollectionMode::ReFungible(_) => {1411 if let CreateItemData::ReFungible(data) = data {14121413 1414 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1415 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1416 } else {1417 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1418 }1419 },1420 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1421 };14221423 Ok(())1424 }14251426 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1427 match data1428 {1429 CreateItemData::NFT(data) => {1430 let item = NftItemType {1431 collection: collection_id,1432 owner,1433 const_data: data.const_data,1434 variable_data: data.variable_data1435 };14361437 Self::add_nft_item(item)?;1438 },1439 CreateItemData::Fungible(_) => {1440 let item = FungibleItemType {1441 collection: collection_id,1442 owner,1443 value: (10 as u128).pow(collection.decimal_points)1444 };14451446 Self::add_fungible_item(item)?;1447 },1448 CreateItemData::ReFungible(data) => {1449 let mut owner_list = Vec::new();1450 let value = (10 as u128).pow(collection.decimal_points);1451 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14521453 let item = ReFungibleItemType {1454 collection: collection_id,1455 owner: owner_list,1456 const_data: data.const_data,1457 variable_data: data.variable_data1458 };14591460 Self::add_refungible_item(item)?;1461 }1462 };146314641465 1466 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14671468 Ok(())1469 }14701471 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1472 let current_index = <ItemListIndex>::get(item.collection)1473 .checked_add(1)1474 .ok_or(Error::<T>::NumOverflow)?;1475 let itemcopy = item.clone();1476 let owner = item.owner.clone();1477 let value = item.value as u64;14781479 Self::add_token_index(item.collection, current_index, owner.clone())?;14801481 <ItemListIndex>::insert(item.collection, current_index);1482 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14831484 1485 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1486 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1487 1488 1489 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1490 .checked_add(value)1491 .ok_or(Error::<T>::NumOverflow)?;1492 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14931494 Ok(())1495 }14961497 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1498 let current_index = <ItemListIndex>::get(item.collection)1499 .checked_add(1)1500 .ok_or(Error::<T>::NumOverflow)?;1501 let itemcopy = item.clone();15021503 let value = item.owner.first().unwrap().fraction as u64;1504 let owner = item.owner.first().unwrap().owner.clone();15051506 Self::add_token_index(item.collection, current_index, owner.clone())?;15071508 <ItemListIndex>::insert(item.collection, current_index);1509 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15101511 1512 let block_number: T::BlockNumber = 0.into();1513 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15141515 1516 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1517 .checked_add(value)1518 .ok_or(Error::<T>::NumOverflow)?;1519 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15201521 Ok(())1522 }15231524 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1525 let current_index = <ItemListIndex>::get(item.collection)1526 .checked_add(1)1527 .ok_or(Error::<T>::NumOverflow)?;15281529 let item_owner = item.owner.clone();1530 let collection_id = item.collection.clone();1531 Self::add_token_index(collection_id, current_index, item.owner.clone())?;15321533 <ItemListIndex>::insert(collection_id, current_index);1534 <NftItemList<T>>::insert(collection_id, current_index, item);15351536 1537 let block_number: T::BlockNumber = 0.into();1538 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15391540 1541 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1542 .checked_add(1)1543 .ok_or(Error::<T>::NumOverflow)?;1544 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15451546 Ok(())1547 }15481549 fn burn_refungible_item(1550 collection_id: u64,1551 item_id: u64,1552 owner: T::AccountId,1553 ) -> DispatchResult {1554 ensure!(1555 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1556 Error::<T>::TokenNotFound1557 );1558 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1559 let item = collection1560 .owner1561 .iter()1562 .filter(|&i| i.owner == owner)1563 .next()1564 .unwrap();1565 Self::remove_token_index(collection_id, item_id, owner.clone())?;15661567 1568 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15691570 1571 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1572 .checked_sub(item.fraction as u64)1573 .ok_or(Error::<T>::NumOverflow)?;1574 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15751576 <ReFungibleItemList<T>>::remove(collection_id, item_id);15771578 Ok(())1579 }15801581 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1582 ensure!(1583 <NftItemList<T>>::contains_key(collection_id, item_id),1584 Error::<T>::TokenNotFound1585 );1586 let item = <NftItemList<T>>::get(collection_id, item_id);1587 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15881589 1590 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15911592 1593 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1594 .checked_sub(1)1595 .ok_or(Error::<T>::NumOverflow)?;1596 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1597 <NftItemList<T>>::remove(collection_id, item_id);15981599 Ok(())1600 }16011602 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1603 ensure!(1604 <FungibleItemList<T>>::contains_key(collection_id, item_id),1605 Error::<T>::TokenNotFound1606 );1607 let item = <FungibleItemList<T>>::get(collection_id, item_id);1608 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16091610 1611 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16121613 1614 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1615 .checked_sub(item.value as u64)1616 .ok_or(Error::<T>::NumOverflow)?;1617 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16181619 <FungibleItemList<T>>::remove(collection_id, item_id);16201621 Ok(())1622 }16231624 fn collection_exists(collection_id: u64) -> DispatchResult {1625 ensure!(1626 <Collection<T>>::contains_key(collection_id),1627 Error::<T>::CollectionNotFound1628 );1629 Ok(())1630 }16311632 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1633 Self::collection_exists(collection_id)?;16341635 let target_collection = <Collection<T>>::get(collection_id);1636 ensure!(1637 subject == target_collection.owner,1638 Error::<T>::NoPermission1639 );16401641 Ok(())1642 }16431644 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1645 let target_collection = <Collection<T>>::get(collection_id);1646 let mut result: bool = subject == target_collection.owner;1647 let exists = <AdminList<T>>::contains_key(collection_id);16481649 if !result & exists {1650 if <AdminList<T>>::get(collection_id).contains(&subject) {1651 result = true1652 }1653 }16541655 result1656 }16571658 fn check_owner_or_admin_permissions(1659 collection_id: u64,1660 subject: T::AccountId,1661 ) -> DispatchResult {1662 Self::collection_exists(collection_id)?;1663 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16641665 ensure!(1666 result,1667 Error::<T>::NoPermission1668 );1669 Ok(())1670 }16711672 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1673 let target_collection = <Collection<T>>::get(collection_id);16741675 match target_collection.mode {1676 CollectionMode::NFT => {1677 <NftItemList<T>>::get(collection_id, item_id).owner == subject1678 }1679 CollectionMode::Fungible(_) => {1680 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1681 }1682 CollectionMode::ReFungible(_) => {1683 <ReFungibleItemList<T>>::get(collection_id, item_id)1684 .owner1685 .iter()1686 .any(|i| i.owner == subject)1687 }1688 CollectionMode::Invalid => false,1689 }1690 }16911692 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1693 let mes = Error::<T>::AddresNotInWhiteList;1694 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1695 let wl = <WhiteList<T>>::get(collection_id);1696 ensure!(wl.contains(address), mes);16971698 Ok(())1699 }17001701 fn transfer_fungible(1702 collection_id: u64,1703 item_id: u64,1704 value: u64,1705 owner: T::AccountId,1706 new_owner: T::AccountId,1707 ) -> DispatchResult {1708 ensure!(1709 <FungibleItemList<T>>::contains_key(collection_id, item_id),1710 Error::<T>::TokenNotFound1711 );17121713 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1714 let amount = full_item.value;17151716 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17171718 1719 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1720 .checked_sub(value)1721 .ok_or(Error::<T>::NumOverflow)?;1722 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17231724 let mut new_owner_account_id = 0;1725 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1726 if new_owner_items.len() > 0 {1727 new_owner_account_id = new_owner_items[0];1728 }17291730 let val64 = value.into();17311732 1733 if amount == val64 && new_owner_account_id == 0 {1734 1735 1736 let mut new_full_item = full_item.clone();1737 new_full_item.owner = new_owner.clone();1738 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17391740 1741 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1742 .checked_add(value)1743 .ok_or(Error::<T>::NumOverflow)?;1744 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17451746 1747 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1748 } else {1749 let mut new_full_item = full_item.clone();1750 new_full_item.value -= val64;17511752 1753 if new_owner_account_id > 0 {1754 1755 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1756 item.value += val64;17571758 1759 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1760 .checked_add(value)1761 .ok_or(Error::<T>::NumOverflow)?;1762 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17631764 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1765 } else {1766 1767 let item = FungibleItemType {1768 collection: collection_id,1769 owner: new_owner.clone(),1770 value: val64,1771 };17721773 Self::add_fungible_item(item)?;1774 }17751776 if amount == val64 {1777 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17781779 1780 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1781 <FungibleItemList<T>>::remove(collection_id, item_id);1782 }17831784 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1785 }17861787 Ok(())1788 }17891790 fn transfer_refungible(1791 collection_id: u64,1792 item_id: u64,1793 value: u64,1794 owner: T::AccountId,1795 new_owner: T::AccountId,1796 ) -> DispatchResult {1797 ensure!(1798 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1799 Error::<T>::TokenNotFound1800 );18011802 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1803 let item = full_item1804 .owner1805 .iter()1806 .filter(|i| i.owner == owner)1807 .next()1808 .ok_or(Error::<T>::NumOverflow)?;1809 let amount = item.fraction;18101811 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18121813 1814 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1815 .checked_sub(value)1816 .ok_or(Error::<T>::NumOverflow)?;1817 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18181819 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1820 .checked_add(value)1821 .ok_or(Error::<T>::NumOverflow)?;1822 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18231824 let old_owner = item.owner.clone();1825 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1826 let val64 = value.into();18271828 1829 if amount == val64 && !new_owner_has_account {1830 1831 1832 let mut new_full_item = full_item.clone();1833 new_full_item1834 .owner1835 .iter_mut()1836 .find(|i| i.owner == owner)1837 .unwrap()1838 .owner = new_owner.clone();1839 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18401841 1842 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1843 } else {1844 let mut new_full_item = full_item.clone();1845 new_full_item1846 .owner1847 .iter_mut()1848 .find(|i| i.owner == owner)1849 .unwrap()1850 .fraction -= val64;18511852 1853 if new_owner_has_account {1854 1855 new_full_item1856 .owner1857 .iter_mut()1858 .find(|i| i.owner == new_owner)1859 .unwrap()1860 .fraction += val64;1861 } else {1862 1863 new_full_item.owner.push(Ownership {1864 owner: new_owner.clone(),1865 fraction: val64,1866 });1867 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1868 }18691870 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1871 }18721873 Ok(())1874 }18751876 fn transfer_nft(1877 collection_id: u64,1878 item_id: u64,1879 sender: T::AccountId,1880 new_owner: T::AccountId,1881 ) -> DispatchResult {1882 ensure!(1883 <NftItemList<T>>::contains_key(collection_id, item_id),1884 Error::<T>::TokenNotFound1885 );18861887 let mut item = <NftItemList<T>>::get(collection_id, item_id);18881889 ensure!(1890 sender == item.owner,1891 Error::<T>::MustBeTokenOwner1892 );18931894 1895 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1896 .checked_sub(1)1897 .ok_or(Error::<T>::NumOverflow)?;1898 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18991900 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1901 .checked_add(1)1902 .ok_or(Error::<T>::NumOverflow)?;1903 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19041905 1906 let old_owner = item.owner.clone();1907 item.owner = new_owner.clone();1908 <NftItemList<T>>::insert(collection_id, item_id, item);19091910 1911 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19121913 1914 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1915 Ok(())1916 }1917 1918 fn item_exists(1919 collection_id: u64,1920 item_id: u64,1921 mode: &CollectionMode1922 ) -> DispatchResult {1923 match mode {1924 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1925 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1926 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1927 _ => ()1928 };1929 1930 Ok(())1931 }19321933 fn set_re_fungible_variable_data(1934 collection_id: u64,1935 item_id: u64,1936 data: Vec<u8>1937 ) -> DispatchResult {1938 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19391940 item.variable_data = data;19411942 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19431944 Ok(())1945 }19461947 fn set_nft_variable_data(1948 collection_id: u64,1949 item_id: u64,1950 data: Vec<u8>1951 ) -> DispatchResult {1952 let mut item = <NftItemList<T>>::get(collection_id, item_id);1953 1954 item.variable_data = data;19551956 <NftItemList<T>>::insert(collection_id, item_id, item);1957 1958 Ok(())1959 }19601961 fn init_collection(item: &CollectionType<T::AccountId>) {1962 1963 assert!(1964 item.decimal_points <= 4,1965 "decimal_points parameter must be lower than 4"1966 );1967 assert!(1968 item.name.len() <= 64,1969 "Collection name can not be longer than 63 char"1970 );1971 assert!(1972 item.name.len() <= 256,1973 "Collection description can not be longer than 255 char"1974 );1975 assert!(1976 item.token_prefix.len() <= 16,1977 "Token prefix can not be longer than 15 char"1978 );19791980 1981 let next_id = CreatedCollectionCount::get()1982 .checked_add(1)1983 .unwrap();19841985 CreatedCollectionCount::put(next_id);1986 }19871988 fn init_nft_token(item: &NftItemType<T::AccountId>) {1989 let current_index = <ItemListIndex>::get(item.collection)1990 .checked_add(1)1991 .unwrap();19921993 let item_owner = item.owner.clone();1994 let collection_id = item.collection.clone();1995 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19961997 <ItemListIndex>::insert(collection_id, current_index);19981999 2000 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2001 .checked_add(1)2002 .unwrap();2003 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2004 }20052006 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2007 let current_index = <ItemListIndex>::get(item.collection)2008 .checked_add(1)2009 .unwrap();2010 let owner = item.owner.clone();2011 let value = item.value as u64;20122013 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20142015 <ItemListIndex>::insert(item.collection, current_index);20162017 2018 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2019 .checked_add(value)2020 .unwrap();2021 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2022 }20232024 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2025 let current_index = <ItemListIndex>::get(item.collection)2026 .checked_add(1)2027 .unwrap();20282029 let value = item.owner.first().unwrap().fraction as u64;2030 let owner = item.owner.first().unwrap().owner.clone();20312032 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20332034 <ItemListIndex>::insert(item.collection, current_index);20352036 2037 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2038 .checked_add(value)2039 .unwrap();2040 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2041 }20422043 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20442045 2046 if <AccountItemCount<T>>::contains_key(owner.clone()) {20472048 2049 let count = <AccountItemCount<T>>::get(owner.clone());2050 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20512052 <AccountItemCount<T>>::insert(owner.clone(), count2053 .checked_add(1)2054 .ok_or(Error::<T>::NumOverflow)?);2055 }2056 else {2057 <AccountItemCount<T>>::insert(owner.clone(), 1);2058 }20592060 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2061 if list_exists {2062 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2063 let item_contains = list.contains(&item_index.clone());20642065 if !item_contains {2066 list.push(item_index.clone());2067 }20682069 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2070 } else {2071 let mut itm = Vec::new();2072 itm.push(item_index.clone());2073 <AddressTokens<T>>::insert(collection_id, owner, itm);2074 2075 }20762077 Ok(())2078 }20792080 fn remove_token_index(2081 collection_id: u64,2082 item_index: u64,2083 owner: T::AccountId,2084 ) -> DispatchResult {20852086 2087 <AccountItemCount<T>>::insert(owner.clone(), 2088 <AccountItemCount<T>>::get(owner.clone())2089 .checked_sub(1)2090 .ok_or(Error::<T>::NumOverflow)?);209120922093 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2094 if list_exists {2095 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2096 let item_contains = list.contains(&item_index.clone());20972098 if item_contains {2099 list.retain(|&item| item != item_index);2100 <AddressTokens<T>>::insert(collection_id, owner, list);2101 }2102 }21032104 Ok(())2105 }21062107 fn move_token_index(2108 collection_id: u64,2109 item_index: u64,2110 old_owner: T::AccountId,2111 new_owner: T::AccountId,2112 ) -> DispatchResult {2113 Self::remove_token_index(collection_id, item_index, old_owner)?;2114 Self::add_token_index(collection_id, item_index, new_owner)?;21152116 Ok(())2117 }2118}2119212021212122212321242125pub type Multiplier = FixedU128;21262127type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2128 <T as system::Trait>::AccountId,2129>>::Balance;2130type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2131 <T as system::Trait>::AccountId,2132>>::NegativeImbalance;2133213421352136#[derive(Encode, Decode, Clone, Eq, PartialEq)]2137pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2138 #[codec(compact)] BalanceOf<T>2139);21402141impl<T: Trait + Send + Sync> sp_std::fmt::Debug2142 for ChargeTransactionPayment<T>2143{2144 #[cfg(feature = "std")]2145 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2146 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2147 }2148 #[cfg(not(feature = "std"))]2149 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2150 Ok(())2151 }2152}21532154impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2155where2156 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2157 BalanceOf<T>: Send + Sync + FixedPointOperand,2158{2159 2160 pub fn from(fee: BalanceOf<T>) -> Self {2161 Self(fee)2162 }21632164 pub fn traditional_fee(2165 len: usize,2166 info: &DispatchInfoOf<T::Call>,2167 tip: BalanceOf<T>,2168 ) -> BalanceOf<T>2169 where2170 T::Call: Dispatchable<Info = DispatchInfo>,2171 {2172 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2173 }21742175 fn withdraw_fee(2176 &self,2177 who: &T::AccountId,2178 call: &T::Call,2179 info: &DispatchInfoOf<T::Call>,2180 len: usize,2181 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2182 let tip = self.0;21832184 2185 2186 2187 2188 2189 2190 2191 let fee = Self::traditional_fee(len, info, tip);21922193 2194 2195 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2196 Some(Call::create_item(collection_id, _properties, _owner)) => {2197 <Collection<T>>::get(collection_id).sponsor2198 }2199 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2200 let _collection_mode = <Collection<T>>::get(collection_id).mode;22012202 2203 let sponsor_transfer = match _collection_mode {2204 CollectionMode::NFT => {2205 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2206 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2207 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2208 if block_number >= limit_time {2209 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2210 true2211 }2212 else {2213 false2214 }2215 }2216 CollectionMode::Fungible(_) => {2217 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2218 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2219 if basket.iter().any(|i| i.address == _new_owner.clone())2220 {2221 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2222 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2223 if block_number >= limit_time {2224 basket.retain(|x| x.address == item.address);2225 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2226 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2227 true2228 }2229 else {2230 false2231 }2232 }2233 else {2234 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2235 true2236 }2237 }2238 CollectionMode::ReFungible(_) => {2239 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2240 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2241 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2242 if block_number >= limit_time {2243 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2244 true2245 } else {2246 false2247 }2248 }2249 _ => {2250 false2251 },2252 };22532254 if !sponsor_transfer {2255 T::AccountId::default()2256 } else {2257 <Collection<T>>::get(collection_id).sponsor2258 }2259 }22602261 _ => T::AccountId::default(),2262 };22632264 2265 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22662267 2268 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22692270 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2271 code_hash,2272 &data,2273 &who,2274 );2275 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22762277 T::AccountId::default()2278 },22792280 2281 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22822283 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22842285 let mut sponsor_transfer = false;2286 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2287 let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2288 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2289 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2290 let limit_time = last_tx_block + rate_limit;22912292 if block_number >= limit_time {2293 <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2294 sponsor_transfer = true;2295 }2296 } else {2297 sponsor_transfer = false;2298 }2299 2300 2301 let mut sp = T::AccountId::default();2302 if sponsor_transfer {2303 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2304 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2305 sp = called_contract;2306 }2307 }2308 }23092310 sp2311 },23122313 _ => sponsor,2314 };23152316 let mut who_pays_fee: T::AccountId = sponsor.clone();2317 if sponsor == T::AccountId::default() {2318 who_pays_fee = who.clone();2319 }23202321 2322 if fee.is_zero() {2323 return Ok((fee, None));2324 }23252326 match <T as transaction_payment::Trait>::Currency::withdraw(2327 &who_pays_fee,2328 fee,2329 if tip.is_zero() {2330 WithdrawReason::TransactionPayment.into()2331 } else {2332 WithdrawReason::TransactionPayment | WithdrawReason::Tip2333 },2334 ExistenceRequirement::KeepAlive,2335 ) {2336 Ok(imbalance) => Ok((fee, Some(imbalance))),2337 Err(_) => Err(InvalidTransaction::Payment.into()),2338 }2339 }2340}234123422343impl<T: Trait + Send + Sync> SignedExtension2344 for ChargeTransactionPayment<T>2345where2346 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2347 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2348{2349 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2350 type AccountId = T::AccountId;2351 type Call = T::Call;2352 type AdditionalSigned = ();2353 type Pre = (2354 BalanceOf<T>,2355 Self::AccountId,2356 Option<NegativeImbalanceOf<T>>,2357 BalanceOf<T>,2358 );2359 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2360 Ok(())2361 }23622363 fn validate(2364 &self,2365 _who: &Self::AccountId,2366 _call: &Self::Call,2367 _info: &DispatchInfoOf<Self::Call>,2368 _len: usize,2369 ) -> TransactionValidity {2370 Ok(ValidTransaction::default())2371 }23722373 fn pre_dispatch(2374 self,2375 who: &Self::AccountId,2376 call: &Self::Call,2377 info: &DispatchInfoOf<Self::Call>,2378 len: usize,2379 ) -> Result<Self::Pre, TransactionValidityError> {2380 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2381 Ok((self.0, who.clone(), imbalance, fee))2382 }23832384 fn post_dispatch(2385 pre: Self::Pre,2386 info: &DispatchInfoOf<Self::Call>,2387 post_info: &PostDispatchInfoOf<Self::Call>,2388 len: usize,2389 _result: &DispatchResult,2390 ) -> Result<(), TransactionValidityError> {2391 let (tip, who, imbalance, fee) = pre;2392 if let Some(payed) = imbalance {2393 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2394 len as u32, info, post_info, tip,2395 );2396 let refund = fee.saturating_sub(actual_fee);2397 let actual_payment =2398 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2399 &who, refund,2400 ) {2401 Ok(refund_imbalance) => {2402 2403 2404 match payed.offset(refund_imbalance) {2405 Ok(actual_payment) => actual_payment,2406 Err(_) => return Err(InvalidTransaction::Payment.into()),2407 }2408 }2409 2410 2411 Err(_) => payed,2412 };2413 let imbalances = actual_payment.split(tip);2414 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2415 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2416 );2417 }2418 Ok(())2419 }2420}24212422