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(identity) T::AccountId => T::AccountId;382 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;383 }384 add_extra_genesis {385 build(|config: &GenesisConfig<T>| {386 387 for (_num, _c) in &config.collection {388 <Module<T>>::init_collection(_c);389 }390391 for (_num, _q, _i) in &config.nft_item_id {392 <Module<T>>::init_nft_token(_i);393 }394395 for (_num, _q, _i) in &config.fungible_item_id {396 <Module<T>>::init_fungible_token(_i);397 }398399 for (_num, _q, _i) in &config.refungible_item_id {400 <Module<T>>::init_refungible_token(_i);401 }402 })403 }404}405406decl_event!(407 pub enum Event<T>408 where409 AccountId = <T as system::Trait>::AccountId,410 {411 412 413 414 415 416 417 418 419 420 Created(u64, u8, AccountId),421422 423 424 425 426 427 428 429 ItemCreated(u64, u64),430431 432 433 434 435 436 437 438 ItemDestroyed(u64, u64),439 }440);441442decl_module! {443 pub struct Module<T: Trait> for enum Call where origin: T::Origin {444445 fn deposit_event() = default;446 type Error = Error<T>;447448 fn on_initialize(now: T::BlockNumber) -> Weight {449450 if ChainVersion::get() < 2451 {452 let value = NextCollectionID::get();453 CreatedCollectionCount::put(value);454 ChainVersion::put(2);455 }456457 0458 }459460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 #[weight = T::WeightInfo::create_collection()]477 pub fn create_collection(origin,478 collection_name: Vec<u16>,479 collection_description: Vec<u16>,480 token_prefix: Vec<u8>,481 mode: CollectionMode) -> DispatchResult {482483 484 let who = ensure_signed(origin)?;485486 let decimal_points = match mode {487 CollectionMode::Fungible(points) => points,488 CollectionMode::ReFungible(points) => points,489 _ => 0490 };491492 493 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);494495 496 ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);497498 let mut name = collection_name.to_vec();499 name.push(0);500 ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);501502 let mut description = collection_description.to_vec();503 description.push(0);504 ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);505506 let mut prefix = token_prefix.to_vec();507 prefix.push(0);508 ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);509510 511 let next_id = CreatedCollectionCount::get()512 .checked_add(1)513 .ok_or(Error::<T>::NumOverflow)?;514515 516 let total = CollectionCount::get()517 .checked_add(1)518 .ok_or(Error::<T>::NumOverflow)?;519520 CreatedCollectionCount::put(next_id);521 CollectionCount::put(total);522523 524 let new_collection = CollectionType {525 owner: who.clone(),526 name: name,527 mode: mode.clone(),528 mint_mode: false,529 access: AccessMode::Normal,530 description: description,531 decimal_points: decimal_points,532 token_prefix: prefix,533 offchain_schema: Vec::new(),534 sponsor: T::AccountId::default(),535 unconfirmed_sponsor: T::AccountId::default(),536 variable_on_chain_schema: Vec::new(),537 const_on_chain_schema: Vec::new(),538 };539540 541 <Collection<T>>::insert(next_id, new_collection);542543 544 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));545546 Ok(())547 }548549 550 551 552 553 554 555 556 557 558 #[weight = T::WeightInfo::destroy_collection()]559 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {560561 let sender = ensure_signed(origin)?;562 Self::check_owner_permissions(collection_id, sender)?;563564 <AddressTokens<T>>::remove_prefix(collection_id);565 <ApprovedList<T>>::remove_prefix(collection_id);566 <Balance<T>>::remove_prefix(collection_id);567 <ItemListIndex>::remove(collection_id);568 <AdminList<T>>::remove(collection_id);569 <Collection<T>>::remove(collection_id);570 <WhiteList<T>>::remove(collection_id);571572 <NftItemList<T>>::remove_prefix(collection_id);573 <FungibleItemList<T>>::remove_prefix(collection_id);574 <ReFungibleItemList<T>>::remove_prefix(collection_id);575576 <NftTransferBasket<T>>::remove_prefix(collection_id);577 <FungibleTransferBasket<T>>::remove_prefix(collection_id);578 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);579580 if CollectionCount::get() > 0581 {582 583 let total = CollectionCount::get()584 .checked_sub(1)585 .ok_or(Error::<T>::NumOverflow)?;586587 CollectionCount::put(total);588 }589590 Ok(())591 }592593 594 595 596 597 598 599 600 601 602 603 604 605 #[weight = T::WeightInfo::add_to_white_list()]606 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{607608 let sender = ensure_signed(origin)?;609 Self::check_owner_or_admin_permissions(collection_id, sender)?;610611 let mut white_list_collection: Vec<T::AccountId>;612 if <WhiteList<T>>::contains_key(collection_id) {613 white_list_collection = <WhiteList<T>>::get(collection_id);614 if !white_list_collection.contains(&address.clone())615 {616 white_list_collection.push(address.clone());617 }618 }619 else {620 white_list_collection = Vec::new();621 white_list_collection.push(address.clone());622 }623624 <WhiteList<T>>::insert(collection_id, white_list_collection);625 Ok(())626 }627628 629 630 631 632 633 634 635 636 637 638 639 640 #[weight = T::WeightInfo::remove_from_white_list()]641 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{642643 let sender = ensure_signed(origin)?;644 Self::check_owner_or_admin_permissions(collection_id, sender)?;645646 if <WhiteList<T>>::contains_key(collection_id) {647 let mut white_list_collection = <WhiteList<T>>::get(collection_id);648 if white_list_collection.contains(&address.clone())649 {650 white_list_collection.retain(|i| *i != address.clone());651 <WhiteList<T>>::insert(collection_id, white_list_collection);652 }653 }654655 Ok(())656 }657658 659 660 661 662 663 664 665 666 667 668 669 #[weight = T::WeightInfo::set_public_access_mode()]670 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult671 {672 let sender = ensure_signed(origin)?;673674 Self::check_owner_permissions(collection_id, sender)?;675 let mut target_collection = <Collection<T>>::get(collection_id);676 target_collection.access = mode;677 <Collection<T>>::insert(collection_id, target_collection);678679 Ok(())680 }681682 683 684 685 686 687 688 689 690 691 692 693 694 695 #[weight = T::WeightInfo::set_mint_permission()]696 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult697 {698 let sender = ensure_signed(origin)?;699700 Self::check_owner_permissions(collection_id, sender)?;701 let mut target_collection = <Collection<T>>::get(collection_id);702 target_collection.mint_mode = mint_permission;703 <Collection<T>>::insert(collection_id, target_collection);704705 Ok(())706 }707708 709 710 711 712 713 714 715 716 717 718 719 #[weight = T::WeightInfo::change_collection_owner()]720 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {721722 let sender = ensure_signed(origin)?;723 Self::check_owner_permissions(collection_id, sender)?;724 let mut target_collection = <Collection<T>>::get(collection_id);725 target_collection.owner = new_owner;726 <Collection<T>>::insert(collection_id, target_collection);727728 Ok(())729 }730731 732 733 734 735 736 737 738 739 740 741 742 743 744 #[weight = T::WeightInfo::add_collection_admin()]745 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {746747 let sender = ensure_signed(origin)?;748 Self::check_owner_or_admin_permissions(collection_id, sender)?;749 let mut admin_arr: Vec<T::AccountId> = Vec::new();750751 if <AdminList<T>>::contains_key(collection_id)752 {753 admin_arr = <AdminList<T>>::get(collection_id);754 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);755 }756757 758 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);759760 admin_arr.push(new_admin_id);761 <AdminList<T>>::insert(collection_id, admin_arr);762763 Ok(())764 }765766 767 768 769 770 771 772 773 774 775 776 777 778 #[weight = T::WeightInfo::remove_collection_admin()]779 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {780781 let sender = ensure_signed(origin)?;782 Self::check_owner_or_admin_permissions(collection_id, sender)?;783784 if <AdminList<T>>::contains_key(collection_id)785 {786 let mut admin_arr = <AdminList<T>>::get(collection_id);787 admin_arr.retain(|i| *i != account_id);788 <AdminList<T>>::insert(collection_id, admin_arr);789 }790791 Ok(())792 }793794 795 796 797 798 799 800 801 802 803 #[weight = T::WeightInfo::set_collection_sponsor()]804 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {805806 let sender = ensure_signed(origin)?;807 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);808809 let mut target_collection = <Collection<T>>::get(collection_id);810 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);811812 target_collection.unconfirmed_sponsor = new_sponsor;813 <Collection<T>>::insert(collection_id, target_collection);814815 Ok(())816 }817818 819 820 821 822 823 824 825 #[weight = T::WeightInfo::confirm_sponsorship()]826 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {827828 let sender = ensure_signed(origin)?;829 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);830831 let mut target_collection = <Collection<T>>::get(collection_id);832 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);833834 target_collection.sponsor = target_collection.unconfirmed_sponsor;835 target_collection.unconfirmed_sponsor = T::AccountId::default();836 <Collection<T>>::insert(collection_id, target_collection);837838 Ok(())839 }840841 842 843 844 845 846 847 848 849 850 #[weight = T::WeightInfo::remove_collection_sponsor()]851 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {852853 let sender = ensure_signed(origin)?;854 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);855856 let mut target_collection = <Collection<T>>::get(collection_id);857 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);858859 target_collection.sponsor = T::AccountId::default();860 <Collection<T>>::insert(collection_id, target_collection);861862 Ok(())863 }864865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888889 #[weight = T::WeightInfo::create_item(data.len())]890 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {891892 let sender = ensure_signed(origin)?;893894 Self::collection_exists(collection_id)?;895896 let target_collection = <Collection<T>>::get(collection_id);897898 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;899 Self::validate_create_item_args(&target_collection, &data)?;900 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;901902 Ok(())903 }904905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 #[weight = T::WeightInfo::create_item(items_data.into_iter()924 .map(|data| { data.len() })925 .sum())]926 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {927928 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);929 let sender = ensure_signed(origin)?;930931 Self::collection_exists(collection_id)?;932 let target_collection = <Collection<T>>::get(collection_id);933934 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;935936 for data in &items_data {937 Self::validate_create_item_args(&target_collection, data)?;938 }939 for data in &items_data {940 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;941 }942943 Ok(())944 }945946 947 948 949 950 951 952 953 954 955 956 957 958 959 #[weight = T::WeightInfo::burn_item()]960 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {961962 let sender = ensure_signed(origin)?;963 Self::collection_exists(collection_id)?;964965 966 let target_collection = <Collection<T>>::get(collection_id);967 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||968 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),969 Error::<T>::NoPermission);970971 if target_collection.access == AccessMode::WhiteList {972 Self::check_white_list(collection_id, &sender)?;973 }974975 match target_collection.mode976 {977 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,978 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,979 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,980 _ => ()981 };982983 984 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));985986 Ok(())987 }988989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 #[weight = T::WeightInfo::transfer()]1013 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10141015 let sender = ensure_signed(origin)?;10161017 1018 let target_collection = <Collection<T>>::get(collection_id);1019 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1020 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1021 Error::<T>::NoPermission);10221023 if target_collection.access == AccessMode::WhiteList {1024 Self::check_white_list(collection_id, &sender)?;1025 Self::check_white_list(collection_id, &recipient)?;1026 }10271028 match target_collection.mode1029 {1030 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1031 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1032 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1033 _ => ()1034 };10351036 Ok(())1037 }10381039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 #[weight = T::WeightInfo::approve()]1055 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10561057 let sender = ensure_signed(origin)?;10581059 1060 let target_collection = <Collection<T>>::get(collection_id);1061 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1062 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1063 Error::<T>::NoPermission);10641065 if target_collection.access == AccessMode::WhiteList {1066 Self::check_white_list(collection_id, &sender)?;1067 Self::check_white_list(collection_id, &approved)?;1068 }10691070 1071 let amount = 100000000;10721073 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1074 if list_exists {10751076 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1077 let item_contains = list.iter().any(|i| i.approved == approved);10781079 if !item_contains {1080 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1081 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1082 }1083 } else {10841085 let mut list = Vec::new();1086 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1087 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1088 }10891090 Ok(())1091 }1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 #[weight = T::WeightInfo::transfer_from()]1113 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11141115 let sender = ensure_signed(origin)?;1116 let mut appoved_transfer = false;11171118 1119 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1120 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1121 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1122 if opt_item.is_some()1123 {1124 appoved_transfer = true;1125 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1126 }1127 }11281129 1130 let target_collection = <Collection<T>>::get(collection_id);1131 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1132 Error::<T>::NoPermission);11331134 if target_collection.access == AccessMode::WhiteList {1135 Self::check_white_list(collection_id, &sender)?;1136 Self::check_white_list(collection_id, &recipient)?;1137 }11381139 1140 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1141 .into_iter().filter(|i| i.approved != sender.clone()).collect();1142 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114311441145 match target_collection.mode1146 {1147 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1148 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1149 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1150 _ => ()1151 };11521153 Ok(())1154 }11551156 1157 #[weight = 0]1158 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11591160 1161 1162 1163 11641165 11661167 11681169 Ok(())1170 }11711172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 #[weight = T::WeightInfo::set_variable_meta_data()]1185 pub fn set_variable_meta_data (1186 origin,1187 collection_id: u64,1188 item_id: u64,1189 data: Vec<u8>1190 ) -> DispatchResult {1191 let sender = ensure_signed(origin)?;1192 1193 Self::collection_exists(collection_id)?;11941195 1196 let target_collection = <Collection<T>>::get(collection_id);1197 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1198 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1199 Error::<T>::NoPermission);12001201 Self::item_exists(collection_id, item_id, &target_collection.mode)?;12021203 match target_collection.mode1204 {1205 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1206 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1207 CollectionMode::Fungible(_) => fail!(Error::<T>::CantSotreMetadataInFungibleTokens),1208 _ => fail!(Error::<T>::UnexpectedCollectionType)1209 };12101211 Ok(())1212 }1213 12141215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 #[weight = T::WeightInfo::set_offchain_schema()]1228 pub fn set_offchain_schema(1229 origin,1230 collection_id: u64,1231 schema: Vec<u8>1232 ) -> DispatchResult {1233 let sender = ensure_signed(origin)?;1234 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12351236 let mut target_collection = <Collection<T>>::get(collection_id);1237 target_collection.offchain_schema = schema;1238 <Collection<T>>::insert(collection_id, target_collection);12391240 Ok(())1241 }12421243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 #[weight = T::WeightInfo::set_const_on_chain_schema()]1256 pub fn set_const_on_chain_schema (1257 origin,1258 collection_id: u64,1259 schema: Vec<u8>1260 ) -> DispatchResult {1261 let sender = ensure_signed(origin)?;1262 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12631264 let mut target_collection = <Collection<T>>::get(collection_id);1265 target_collection.const_on_chain_schema = schema;1266 <Collection<T>>::insert(collection_id, target_collection);12671268 Ok(())1269 }12701271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 #[weight = T::WeightInfo::set_const_on_chain_schema()]1284 pub fn set_variable_on_chain_schema (1285 origin,1286 collection_id: u64,1287 schema: Vec<u8>1288 ) -> DispatchResult {1289 let sender = ensure_signed(origin)?;1290 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12911292 let mut target_collection = <Collection<T>>::get(collection_id);1293 target_collection.variable_on_chain_schema = schema;1294 <Collection<T>>::insert(collection_id, target_collection);12951296 Ok(())1297 }12981299 1300 #[weight = 0]1301 pub fn set_chain_limits(1302 origin,1303 limits: ChainLimits1304 ) -> DispatchResult {1305 ensure_root(origin)?;1306 <ChainLimit>::put(limits);1307 Ok(())1308 }13091310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 #[weight = 0]1322 pub fn enable_contract_sponsoring(1323 origin,1324 contract_address: T::AccountId,1325 enable: bool1326 ) -> DispatchResult {1327 let sender = ensure_signed(origin)?;1328 let mut is_owner = false;1329 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1330 let owner = <ContractOwner<T>>::get(&contract_address);1331 is_owner = sender == owner;1332 }1333 ensure!(is_owner, Error::<T>::NoPermission);13341335 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1336 Ok(())1337 }1338 }1339}13401341impl<T: Trait> Module<T> {13421343 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13441345 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1346 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1347 Self::check_white_list(collection_id, owner)?;1348 Self::check_white_list(collection_id, sender)?;1349 }13501351 Ok(())1352 }13531354 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1355 match target_collection.mode1356 {1357 CollectionMode::NFT => {1358 if let CreateItemData::NFT(data) = data {1359 1360 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1361 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1362 } else {1363 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1364 }1365 },1366 CollectionMode::Fungible(_) => {1367 if let CreateItemData::Fungible(_) = data {1368 } else {1369 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1370 }1371 },1372 CollectionMode::ReFungible(_) => {1373 if let CreateItemData::ReFungible(data) = data {13741375 1376 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1377 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1378 } else {1379 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1380 }1381 },1382 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1383 };13841385 Ok(())1386 }13871388 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1389 match data1390 {1391 CreateItemData::NFT(data) => {1392 let item = NftItemType {1393 collection: collection_id,1394 owner,1395 const_data: data.const_data,1396 variable_data: data.variable_data1397 };13981399 Self::add_nft_item(item)?;1400 },1401 CreateItemData::Fungible(_) => {1402 let item = FungibleItemType {1403 collection: collection_id,1404 owner,1405 value: (10 as u128).pow(collection.decimal_points)1406 };14071408 Self::add_fungible_item(item)?;1409 },1410 CreateItemData::ReFungible(data) => {1411 let mut owner_list = Vec::new();1412 let value = (10 as u128).pow(collection.decimal_points);1413 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14141415 let item = ReFungibleItemType {1416 collection: collection_id,1417 owner: owner_list,1418 const_data: data.const_data,1419 variable_data: data.variable_data1420 };14211422 Self::add_refungible_item(item)?;1423 }1424 };142514261427 1428 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14291430 Ok(())1431 }14321433 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1434 let current_index = <ItemListIndex>::get(item.collection)1435 .checked_add(1)1436 .ok_or(Error::<T>::NumOverflow)?;1437 let itemcopy = item.clone();1438 let owner = item.owner.clone();1439 let value = item.value as u64;14401441 Self::add_token_index(item.collection, current_index, owner.clone())?;14421443 <ItemListIndex>::insert(item.collection, current_index);1444 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14451446 1447 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1448 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1449 1450 1451 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1452 .checked_add(value)1453 .ok_or(Error::<T>::NumOverflow)?;1454 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14551456 Ok(())1457 }14581459 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1460 let current_index = <ItemListIndex>::get(item.collection)1461 .checked_add(1)1462 .ok_or(Error::<T>::NumOverflow)?;1463 let itemcopy = item.clone();14641465 let value = item.owner.first().unwrap().fraction as u64;1466 let owner = item.owner.first().unwrap().owner.clone();14671468 Self::add_token_index(item.collection, current_index, owner.clone())?;14691470 <ItemListIndex>::insert(item.collection, current_index);1471 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14721473 1474 let block_number: T::BlockNumber = 0.into();1475 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14761477 1478 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1479 .checked_add(value)1480 .ok_or(Error::<T>::NumOverflow)?;1481 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14821483 Ok(())1484 }14851486 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1487 let current_index = <ItemListIndex>::get(item.collection)1488 .checked_add(1)1489 .ok_or(Error::<T>::NumOverflow)?;14901491 let item_owner = item.owner.clone();1492 let collection_id = item.collection.clone();1493 Self::add_token_index(collection_id, current_index, item.owner.clone())?;14941495 <ItemListIndex>::insert(collection_id, current_index);1496 <NftItemList<T>>::insert(collection_id, current_index, item);14971498 1499 let block_number: T::BlockNumber = 0.into();1500 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15011502 1503 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1504 .checked_add(1)1505 .ok_or(Error::<T>::NumOverflow)?;1506 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15071508 Ok(())1509 }15101511 fn burn_refungible_item(1512 collection_id: u64,1513 item_id: u64,1514 owner: T::AccountId,1515 ) -> DispatchResult {1516 ensure!(1517 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1518 Error::<T>::TokenNotFound1519 );1520 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1521 let item = collection1522 .owner1523 .iter()1524 .filter(|&i| i.owner == owner)1525 .next()1526 .unwrap();1527 Self::remove_token_index(collection_id, item_id, owner.clone())?;15281529 1530 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15311532 1533 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1534 .checked_sub(item.fraction as u64)1535 .ok_or(Error::<T>::NumOverflow)?;1536 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15371538 <ReFungibleItemList<T>>::remove(collection_id, item_id);15391540 Ok(())1541 }15421543 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1544 ensure!(1545 <NftItemList<T>>::contains_key(collection_id, item_id),1546 Error::<T>::TokenNotFound1547 );1548 let item = <NftItemList<T>>::get(collection_id, item_id);1549 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15501551 1552 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15531554 1555 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1556 .checked_sub(1)1557 .ok_or(Error::<T>::NumOverflow)?;1558 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1559 <NftItemList<T>>::remove(collection_id, item_id);15601561 Ok(())1562 }15631564 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1565 ensure!(1566 <FungibleItemList<T>>::contains_key(collection_id, item_id),1567 Error::<T>::TokenNotFound1568 );1569 let item = <FungibleItemList<T>>::get(collection_id, item_id);1570 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15711572 1573 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15741575 1576 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1577 .checked_sub(item.value as u64)1578 .ok_or(Error::<T>::NumOverflow)?;1579 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15801581 <FungibleItemList<T>>::remove(collection_id, item_id);15821583 Ok(())1584 }15851586 fn collection_exists(collection_id: u64) -> DispatchResult {1587 ensure!(1588 <Collection<T>>::contains_key(collection_id),1589 Error::<T>::CollectionNotFound1590 );1591 Ok(())1592 }15931594 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1595 Self::collection_exists(collection_id)?;15961597 let target_collection = <Collection<T>>::get(collection_id);1598 ensure!(1599 subject == target_collection.owner,1600 Error::<T>::NoPermission1601 );16021603 Ok(())1604 }16051606 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1607 let target_collection = <Collection<T>>::get(collection_id);1608 let mut result: bool = subject == target_collection.owner;1609 let exists = <AdminList<T>>::contains_key(collection_id);16101611 if !result & exists {1612 if <AdminList<T>>::get(collection_id).contains(&subject) {1613 result = true1614 }1615 }16161617 result1618 }16191620 fn check_owner_or_admin_permissions(1621 collection_id: u64,1622 subject: T::AccountId,1623 ) -> DispatchResult {1624 Self::collection_exists(collection_id)?;1625 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16261627 ensure!(1628 result,1629 Error::<T>::NoPermission1630 );1631 Ok(())1632 }16331634 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1635 let target_collection = <Collection<T>>::get(collection_id);16361637 match target_collection.mode {1638 CollectionMode::NFT => {1639 <NftItemList<T>>::get(collection_id, item_id).owner == subject1640 }1641 CollectionMode::Fungible(_) => {1642 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1643 }1644 CollectionMode::ReFungible(_) => {1645 <ReFungibleItemList<T>>::get(collection_id, item_id)1646 .owner1647 .iter()1648 .any(|i| i.owner == subject)1649 }1650 CollectionMode::Invalid => false,1651 }1652 }16531654 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1655 let mes = Error::<T>::AddresNotInWhiteList;1656 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1657 let wl = <WhiteList<T>>::get(collection_id);1658 ensure!(wl.contains(address), mes);16591660 Ok(())1661 }16621663 fn transfer_fungible(1664 collection_id: u64,1665 item_id: u64,1666 value: u64,1667 owner: T::AccountId,1668 new_owner: T::AccountId,1669 ) -> DispatchResult {1670 ensure!(1671 <FungibleItemList<T>>::contains_key(collection_id, item_id),1672 Error::<T>::TokenNotFound1673 );16741675 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1676 let amount = full_item.value;16771678 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);16791680 1681 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1682 .checked_sub(value)1683 .ok_or(Error::<T>::NumOverflow)?;1684 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16851686 let mut new_owner_account_id = 0;1687 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1688 if new_owner_items.len() > 0 {1689 new_owner_account_id = new_owner_items[0];1690 }16911692 let val64 = value.into();16931694 1695 if amount == val64 && new_owner_account_id == 0 {1696 1697 1698 let mut new_full_item = full_item.clone();1699 new_full_item.owner = new_owner.clone();1700 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17011702 1703 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1704 .checked_add(value)1705 .ok_or(Error::<T>::NumOverflow)?;1706 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17071708 1709 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1710 } else {1711 let mut new_full_item = full_item.clone();1712 new_full_item.value -= val64;17131714 1715 if new_owner_account_id > 0 {1716 1717 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1718 item.value += val64;17191720 1721 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1722 .checked_add(value)1723 .ok_or(Error::<T>::NumOverflow)?;1724 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17251726 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1727 } else {1728 1729 let item = FungibleItemType {1730 collection: collection_id,1731 owner: new_owner.clone(),1732 value: val64,1733 };17341735 Self::add_fungible_item(item)?;1736 }17371738 if amount == val64 {1739 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17401741 1742 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1743 <FungibleItemList<T>>::remove(collection_id, item_id);1744 }17451746 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1747 }17481749 Ok(())1750 }17511752 fn transfer_refungible(1753 collection_id: u64,1754 item_id: u64,1755 value: u64,1756 owner: T::AccountId,1757 new_owner: T::AccountId,1758 ) -> DispatchResult {1759 ensure!(1760 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1761 Error::<T>::TokenNotFound1762 );17631764 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1765 let item = full_item1766 .owner1767 .iter()1768 .filter(|i| i.owner == owner)1769 .next()1770 .ok_or(Error::<T>::NumOverflow)?;1771 let amount = item.fraction;17721773 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17741775 1776 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1777 .checked_sub(value)1778 .ok_or(Error::<T>::NumOverflow)?;1779 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17801781 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1782 .checked_add(value)1783 .ok_or(Error::<T>::NumOverflow)?;1784 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17851786 let old_owner = item.owner.clone();1787 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1788 let val64 = value.into();17891790 1791 if amount == val64 && !new_owner_has_account {1792 1793 1794 let mut new_full_item = full_item.clone();1795 new_full_item1796 .owner1797 .iter_mut()1798 .find(|i| i.owner == owner)1799 .unwrap()1800 .owner = new_owner.clone();1801 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18021803 1804 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1805 } else {1806 let mut new_full_item = full_item.clone();1807 new_full_item1808 .owner1809 .iter_mut()1810 .find(|i| i.owner == owner)1811 .unwrap()1812 .fraction -= val64;18131814 1815 if new_owner_has_account {1816 1817 new_full_item1818 .owner1819 .iter_mut()1820 .find(|i| i.owner == new_owner)1821 .unwrap()1822 .fraction += val64;1823 } else {1824 1825 new_full_item.owner.push(Ownership {1826 owner: new_owner.clone(),1827 fraction: val64,1828 });1829 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1830 }18311832 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1833 }18341835 Ok(())1836 }18371838 fn transfer_nft(1839 collection_id: u64,1840 item_id: u64,1841 sender: T::AccountId,1842 new_owner: T::AccountId,1843 ) -> DispatchResult {1844 ensure!(1845 <NftItemList<T>>::contains_key(collection_id, item_id),1846 Error::<T>::TokenNotFound1847 );18481849 let mut item = <NftItemList<T>>::get(collection_id, item_id);18501851 ensure!(1852 sender == item.owner,1853 Error::<T>::MustBeTokenOwner1854 );18551856 1857 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1858 .checked_sub(1)1859 .ok_or(Error::<T>::NumOverflow)?;1860 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18611862 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1863 .checked_add(1)1864 .ok_or(Error::<T>::NumOverflow)?;1865 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18661867 1868 let old_owner = item.owner.clone();1869 item.owner = new_owner.clone();1870 <NftItemList<T>>::insert(collection_id, item_id, item);18711872 1873 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18741875 1876 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1877 Ok(())1878 }1879 1880 fn item_exists(1881 collection_id: u64,1882 item_id: u64,1883 mode: &CollectionMode1884 ) -> DispatchResult {1885 match mode {1886 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1887 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1888 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1889 _ => ()1890 };1891 1892 Ok(())1893 }18941895 fn set_re_fungible_variable_data(1896 collection_id: u64,1897 item_id: u64,1898 data: Vec<u8>1899 ) -> DispatchResult {1900 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19011902 item.variable_data = data;19031904 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19051906 Ok(())1907 }19081909 fn set_nft_variable_data(1910 collection_id: u64,1911 item_id: u64,1912 data: Vec<u8>1913 ) -> DispatchResult {1914 let mut item = <NftItemList<T>>::get(collection_id, item_id);1915 1916 item.variable_data = data;19171918 <NftItemList<T>>::insert(collection_id, item_id, item);1919 1920 Ok(())1921 }19221923 fn init_collection(item: &CollectionType<T::AccountId>) {1924 1925 assert!(1926 item.decimal_points <= 4,1927 "decimal_points parameter must be lower than 4"1928 );1929 assert!(1930 item.name.len() <= 64,1931 "Collection name can not be longer than 63 char"1932 );1933 assert!(1934 item.name.len() <= 256,1935 "Collection description can not be longer than 255 char"1936 );1937 assert!(1938 item.token_prefix.len() <= 16,1939 "Token prefix can not be longer than 15 char"1940 );19411942 1943 let next_id = CreatedCollectionCount::get()1944 .checked_add(1)1945 .unwrap();19461947 CreatedCollectionCount::put(next_id);1948 }19491950 fn init_nft_token(item: &NftItemType<T::AccountId>) {1951 let current_index = <ItemListIndex>::get(item.collection)1952 .checked_add(1)1953 .unwrap();19541955 let item_owner = item.owner.clone();1956 let collection_id = item.collection.clone();1957 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19581959 <ItemListIndex>::insert(collection_id, current_index);19601961 1962 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1963 .checked_add(1)1964 .unwrap();1965 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1966 }19671968 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1969 let current_index = <ItemListIndex>::get(item.collection)1970 .checked_add(1)1971 .unwrap();1972 let owner = item.owner.clone();1973 let value = item.value as u64;19741975 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19761977 <ItemListIndex>::insert(item.collection, current_index);19781979 1980 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1981 .checked_add(value)1982 .unwrap();1983 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1984 }19851986 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1987 let current_index = <ItemListIndex>::get(item.collection)1988 .checked_add(1)1989 .unwrap();19901991 let value = item.owner.first().unwrap().fraction as u64;1992 let owner = item.owner.first().unwrap().owner.clone();19931994 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19951996 <ItemListIndex>::insert(item.collection, current_index);19971998 1999 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2000 .checked_add(value)2001 .unwrap();2002 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2003 }20042005 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20062007 2008 if <AccountItemCount<T>>::contains_key(owner.clone()) {20092010 2011 let count = <AccountItemCount<T>>::get(owner.clone());2012 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20132014 <AccountItemCount<T>>::insert(owner.clone(), count2015 .checked_add(1)2016 .ok_or(Error::<T>::NumOverflow)?);2017 }2018 else {2019 <AccountItemCount<T>>::insert(owner.clone(), 1);2020 }20212022 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2023 if list_exists {2024 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2025 let item_contains = list.contains(&item_index.clone());20262027 if !item_contains {2028 list.push(item_index.clone());2029 }20302031 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2032 } else {2033 let mut itm = Vec::new();2034 itm.push(item_index.clone());2035 <AddressTokens<T>>::insert(collection_id, owner, itm);2036 2037 }20382039 Ok(())2040 }20412042 fn remove_token_index(2043 collection_id: u64,2044 item_index: u64,2045 owner: T::AccountId,2046 ) -> DispatchResult {20472048 2049 <AccountItemCount<T>>::insert(owner.clone(), 2050 <AccountItemCount<T>>::get(owner.clone())2051 .checked_sub(1)2052 .ok_or(Error::<T>::NumOverflow)?);205320542055 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2056 if list_exists {2057 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2058 let item_contains = list.contains(&item_index.clone());20592060 if item_contains {2061 list.retain(|&item| item != item_index);2062 <AddressTokens<T>>::insert(collection_id, owner, list);2063 }2064 }20652066 Ok(())2067 }20682069 fn move_token_index(2070 collection_id: u64,2071 item_index: u64,2072 old_owner: T::AccountId,2073 new_owner: T::AccountId,2074 ) -> DispatchResult {2075 Self::remove_token_index(collection_id, item_index, old_owner)?;2076 Self::add_token_index(collection_id, item_index, new_owner)?;20772078 Ok(())2079 }2080}2081208220832084208520862087pub type Multiplier = FixedU128;20882089type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2090 <T as system::Trait>::AccountId,2091>>::Balance;2092type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2093 <T as system::Trait>::AccountId,2094>>::NegativeImbalance;2095209620972098#[derive(Encode, Decode, Clone, Eq, PartialEq)]2099pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2100 #[codec(compact)] BalanceOf<T>2101);21022103impl<T: Trait + Send + Sync> sp_std::fmt::Debug2104 for ChargeTransactionPayment<T>2105{2106 #[cfg(feature = "std")]2107 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2108 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2109 }2110 #[cfg(not(feature = "std"))]2111 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2112 Ok(())2113 }2114}21152116impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2117where2118 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2119 BalanceOf<T>: Send + Sync + FixedPointOperand,2120{2121 2122 pub fn from(fee: BalanceOf<T>) -> Self {2123 Self(fee)2124 }21252126 pub fn traditional_fee(2127 len: usize,2128 info: &DispatchInfoOf<T::Call>,2129 tip: BalanceOf<T>,2130 ) -> BalanceOf<T>2131 where2132 T::Call: Dispatchable<Info = DispatchInfo>,2133 {2134 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2135 }21362137 fn withdraw_fee(2138 &self,2139 who: &T::AccountId,2140 call: &T::Call,2141 info: &DispatchInfoOf<T::Call>,2142 len: usize,2143 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2144 let tip = self.0;21452146 2147 2148 2149 2150 2151 2152 2153 let fee = Self::traditional_fee(len, info, tip);21542155 2156 2157 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2158 Some(Call::create_item(collection_id, _properties, _owner)) => {2159 <Collection<T>>::get(collection_id).sponsor2160 }2161 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2162 let _collection_mode = <Collection<T>>::get(collection_id).mode;21632164 2165 let sponsor_transfer = match _collection_mode {2166 CollectionMode::NFT => {2167 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2168 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2169 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2170 if block_number >= limit_time {2171 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2172 true2173 }2174 else {2175 false2176 }2177 }2178 CollectionMode::Fungible(_) => {2179 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2180 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2181 if basket.iter().any(|i| i.address == _new_owner.clone())2182 {2183 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2184 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2185 if block_number >= limit_time {2186 basket.retain(|x| x.address == item.address);2187 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2188 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2189 true2190 }2191 else {2192 false2193 }2194 }2195 else {2196 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2197 true2198 }2199 }2200 CollectionMode::ReFungible(_) => {2201 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2202 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2203 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2204 if block_number >= limit_time {2205 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2206 true2207 } else {2208 false2209 }2210 }2211 _ => {2212 false2213 },2214 };22152216 if !sponsor_transfer {2217 T::AccountId::default()2218 } else {2219 <Collection<T>>::get(collection_id).sponsor2220 }2221 }22222223 _ => T::AccountId::default(),2224 };22252226 2227 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22282229 2230 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22312232 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2233 code_hash,2234 &data,2235 &who,2236 );2237 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22382239 T::AccountId::default()2240 },22412242 2243 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22442245 let mut sp = T::AccountId::default();2246 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2247 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2248 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2249 sp = called_contract;2250 }2251 }22522253 sp2254 },22552256 _ => sponsor,2257 };22582259 let mut who_pays_fee: T::AccountId = sponsor.clone();2260 if sponsor == T::AccountId::default() {2261 who_pays_fee = who.clone();2262 }22632264 2265 if fee.is_zero() {2266 return Ok((fee, None));2267 }22682269 match <T as transaction_payment::Trait>::Currency::withdraw(2270 &who_pays_fee,2271 fee,2272 if tip.is_zero() {2273 WithdrawReason::TransactionPayment.into()2274 } else {2275 WithdrawReason::TransactionPayment | WithdrawReason::Tip2276 },2277 ExistenceRequirement::KeepAlive,2278 ) {2279 Ok(imbalance) => Ok((fee, Some(imbalance))),2280 Err(_) => Err(InvalidTransaction::Payment.into()),2281 }2282 }2283}228422852286impl<T: Trait + Send + Sync> SignedExtension2287 for ChargeTransactionPayment<T>2288where2289 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2290 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2291{2292 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2293 type AccountId = T::AccountId;2294 type Call = T::Call;2295 type AdditionalSigned = ();2296 type Pre = (2297 BalanceOf<T>,2298 Self::AccountId,2299 Option<NegativeImbalanceOf<T>>,2300 BalanceOf<T>,2301 );2302 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2303 Ok(())2304 }23052306 fn validate(2307 &self,2308 _who: &Self::AccountId,2309 _call: &Self::Call,2310 _info: &DispatchInfoOf<Self::Call>,2311 _len: usize,2312 ) -> TransactionValidity {2313 Ok(ValidTransaction::default())2314 }23152316 fn pre_dispatch(2317 self,2318 who: &Self::AccountId,2319 call: &Self::Call,2320 info: &DispatchInfoOf<Self::Call>,2321 len: usize,2322 ) -> Result<Self::Pre, TransactionValidityError> {2323 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2324 Ok((self.0, who.clone(), imbalance, fee))2325 }23262327 fn post_dispatch(2328 pre: Self::Pre,2329 info: &DispatchInfoOf<Self::Call>,2330 post_info: &PostDispatchInfoOf<Self::Call>,2331 len: usize,2332 _result: &DispatchResult,2333 ) -> Result<(), TransactionValidityError> {2334 let (tip, who, imbalance, fee) = pre;2335 if let Some(payed) = imbalance {2336 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2337 len as u32, info, post_info, tip,2338 );2339 let refund = fee.saturating_sub(actual_fee);2340 let actual_payment =2341 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2342 &who, refund,2343 ) {2344 Ok(refund_imbalance) => {2345 2346 2347 match payed.offset(refund_imbalance) {2348 Ok(actual_payment) => actual_payment,2349 Err(_) => return Err(InvalidTransaction::Payment.into()),2350 }2351 }2352 2353 2354 Err(_) => payed,2355 };2356 let imbalances = actual_payment.split(tip);2357 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2358 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2359 );2360 }2361 Ok(())2362 }2363}23642365