123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18 construct_runtime, decl_event, decl_module, decl_storage, decl_error,19 dispatch::DispatchResult,20 ensure, fail, parameter_types,21 traits::{22 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23 Randomness, IsSubType, WithdrawReasons,24 },25 weights::{26 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28 WeightToFeePolynomial, DispatchClass,29 },30 StorageValue,31 transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_runtime::sp_std::prelude::Vec;36use core::ops::{Deref, DerefMut};37use nft_data_structs::{38 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,39 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,40 CollectionId, CollectionMode, TokenId, 41 SchemaVersion, SponsorshipState, Ownership,42 NftItemType, FungibleItemType, ReFungibleItemType43};4445#[cfg(test)]46mod mock;4748#[cfg(test)]49mod tests;5051mod default_weights;5253#[cfg(feature = "runtime-benchmarks")]54mod benchmarking;5556pub trait WeightInfo {57 fn create_collection() -> Weight;58 fn destroy_collection() -> Weight;59 fn add_to_white_list() -> Weight;60 fn remove_from_white_list() -> Weight;61 fn set_public_access_mode() -> Weight;62 fn set_mint_permission() -> Weight;63 fn change_collection_owner() -> Weight;64 fn add_collection_admin() -> Weight;65 fn remove_collection_admin() -> Weight;66 fn set_collection_sponsor() -> Weight;67 fn confirm_sponsorship() -> Weight;68 fn remove_collection_sponsor() -> Weight;69 fn create_item(s: usize) -> Weight;70 fn burn_item() -> Weight;71 fn transfer() -> Weight;72 fn approve() -> Weight;73 fn transfer_from() -> Weight;74 fn set_offchain_schema() -> Weight;75 fn set_const_on_chain_schema() -> Weight;76 fn set_variable_on_chain_schema() -> Weight;77 fn set_variable_meta_data() -> Weight;78 fn enable_contract_sponsoring() -> Weight;79 fn set_schema_version() -> Weight;80 fn set_chain_limits() -> Weight;81 fn set_contract_sponsoring_rate_limit() -> Weight;82 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;83 fn toggle_contract_white_list() -> Weight;84 fn add_to_contract_white_list() -> Weight;85 fn remove_from_contract_white_list() -> Weight;86 fn set_collection_limits() -> Weight;87}8889decl_error! {90 91 pub enum Error for Module<T: Config> {92 93 TotalCollectionsLimitExceeded,94 95 CollectionDecimalPointLimitExceeded, 96 97 CollectionNameLimitExceeded, 98 99 CollectionDescriptionLimitExceeded, 100 101 CollectionTokenPrefixLimitExceeded,102 103 CollectionNotFound,104 105 TokenNotFound,106 107 AdminNotFound,108 109 NumOverflow, 110 111 AlreadyAdmin, 112 113 NoPermission,114 115 ConfirmUnsetSponsorFail,116 117 PublicMintingNotAllowed,118 119 MustBeTokenOwner,120 121 TokenValueTooLow,122 123 NftSizeLimitExceeded,124 125 ApproveNotFound,126 127 TokenValueNotEnough,128 129 ApproveRequired,130 131 AddresNotInWhiteList,132 133 CollectionAdminsLimitExceeded,134 135 AddressOwnershipLimitExceeded,136 137 EmptyArgument,138 139 TokenConstDataLimitExceeded,140 141 TokenVariableDataLimitExceeded,142 143 NotNftDataUsedToMintNftCollectionToken,144 145 NotFungibleDataUsedToMintFungibleCollectionToken,146 147 NotReFungibleDataUsedToMintReFungibleCollectionToken,148 149 UnexpectedCollectionType,150 151 CantStoreMetadataInFungibleTokens,152 153 CollectionTokenLimitExceeded,154 155 AccountTokenLimitExceeded,156 157 CollectionLimitBoundsExceeded,158 159 OwnerPermissionsCantBeReverted,160 161 SchemaDataLimitExceeded,162 163 WrongRefungiblePieces,164 165 BadCreateRefungibleCall,166 }167}168169pub struct CollectionHandle<T: system::Config> {170 pub id: CollectionId,171 pub collection: Collection<T>,172}173174impl<T: frame_system::Config> Deref for CollectionHandle<T> {175 type Target = Collection<T>;176177 fn deref(&self) -> &Self::Target {178 &self.collection179 }180}181182impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {183 fn deref_mut(&mut self) -> &mut Self::Target {184 &mut self.collection185 }186}187188pub trait Config: system::Config + Sized {189 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;190191 192 type WeightInfo: WeightInfo;193194 type Currency: Currency<Self::AccountId>;195 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;196 type TreasuryAccountId: Get<Self::AccountId>;197}198199200201202203204205206207208209210211212213214215216217218219220221decl_storage! {222 trait Store for Module<T: Config> as Nft {223224 225 226 CreatedCollectionCount: u32;227 228 ChainVersion: u64;229 230 231 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;232 233234 235 pub ChainLimit get(fn chain_limit) config(): ChainLimits;236 237238 239 240 241 DestroyedCollectionCount: u32;242 243 244 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;245 246247 248 249 250 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;251 252 253 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;254 255 256 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;257 258259 260 261 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;262263 264 265 266 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;267268 269 270 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;271 272 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;273 274 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;275 276277 278 279 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;280 281282 283 284 285 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;286 287 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;288 289 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;290 291 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;292 293294 295 296 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;297 298 299 300 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;301 302 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;303 304 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;305 306 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;307 308 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 309 310 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 311 312 }313 add_extra_genesis {314 build(|config: &GenesisConfig<T>| {315 316 for (_num, _c) in &config.collection_id {317 <Module<T>>::init_collection(_c);318 }319320 for (_num, _c, _i) in &config.nft_item_id {321 <Module<T>>::init_nft_token(*_c, _i);322 }323324 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {325 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);326 }327328 for (_num, _c, _i) in &config.refungible_item_id {329 <Module<T>>::init_refungible_token(*_c, _i);330 }331 })332 }333}334335decl_event!(336 pub enum Event<T>337 where338 AccountId = <T as system::Config>::AccountId,339 {340 341 342 343 344 345 346 347 348 349 CollectionCreated(CollectionId, u8, AccountId),350351 352 353 354 355 356 357 358 359 360 ItemCreated(CollectionId, TokenId, AccountId),361362 363 364 365 366 367 368 369 ItemDestroyed(CollectionId, TokenId),370371 372 373 374 375 376 377 378 379 380 381 382 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),383384 385 386 387 388 389 390 391 392 393 Approved(CollectionId, TokenId, AccountId, AccountId, u128),394 }395);396397decl_module! {398 pub struct Module<T: Config> for enum Call 399 where 400 origin: T::Origin401 {402 fn deposit_event() = default;403 type Error = Error<T>;404405 fn on_initialize(_now: T::BlockNumber) -> Weight {406 0407 }408409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 #[weight = <T as Config>::WeightInfo::create_collection()]426 #[transactional]427 pub fn create_collection(origin,428 collection_name: Vec<u16>,429 collection_description: Vec<u16>,430 token_prefix: Vec<u8>,431 mode: CollectionMode) -> DispatchResult {432433 434 let who = ensure_signed(origin)?;435436 437 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();438 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(439 &T::TreasuryAccountId::get(),440 T::CollectionCreationPrice::get(),441 ));442 <T as Config>::Currency::settle(443 &who,444 imbalance,445 WithdrawReasons::TRANSFER,446 ExistenceRequirement::KeepAlive,447 ).map_err(|_| Error::<T>::NoPermission)?;448449 let decimal_points = match mode {450 CollectionMode::Fungible(points) => points,451 _ => 0452 };453454 let chain_limit = ChainLimit::get();455456 let created_count = CreatedCollectionCount::get();457 let destroyed_count = DestroyedCollectionCount::get();458459 460 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);461462 463 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);464 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);465 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);466 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);467468 469 let next_id = created_count470 .checked_add(1)471 .ok_or(Error::<T>::NumOverflow)?;472473 CreatedCollectionCount::put(next_id);474475 let limits = CollectionLimits {476 sponsored_data_size: chain_limit.custom_data_limit,477 ..Default::default()478 };479480 481 let new_collection = Collection {482 owner: who.clone(),483 name: collection_name,484 mode: mode.clone(),485 mint_mode: false,486 access: AccessMode::Normal,487 description: collection_description,488 decimal_points: decimal_points,489 token_prefix: token_prefix,490 offchain_schema: Vec::new(),491 schema_version: SchemaVersion::ImageURL,492 sponsorship: SponsorshipState::Disabled,493 variable_on_chain_schema: Vec::new(),494 const_on_chain_schema: Vec::new(),495 limits,496 };497498 499 <CollectionById<T>>::insert(next_id, new_collection);500501 502 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));503504 Ok(())505 }506507 508 509 510 511 512 513 514 515 516 #[weight = <T as Config>::WeightInfo::destroy_collection()]517 #[transactional]518 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {519520 let sender = ensure_signed(origin)?;521 let collection = Self::get_collection(collection_id)?;522 Self::check_owner_permissions(&collection, sender)?;523 if !collection.limits.owner_can_destroy {524 fail!(Error::<T>::NoPermission);525 }526527 <AddressTokens<T>>::remove_prefix(collection_id);528 <Allowances<T>>::remove_prefix(collection_id);529 <Balance<T>>::remove_prefix(collection_id);530 <ItemListIndex>::remove(collection_id);531 <AdminList<T>>::remove(collection_id);532 <CollectionById<T>>::remove(collection_id);533 <WhiteList<T>>::remove_prefix(collection_id);534535 <NftItemList<T>>::remove_prefix(collection_id);536 <FungibleItemList<T>>::remove_prefix(collection_id);537 <ReFungibleItemList<T>>::remove_prefix(collection_id);538539 <NftTransferBasket<T>>::remove_prefix(collection_id);540 <FungibleTransferBasket<T>>::remove_prefix(collection_id);541 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);542543 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);544545 DestroyedCollectionCount::put(DestroyedCollectionCount::get()546 .checked_add(1)547 .ok_or(Error::<T>::NumOverflow)?);548549 Ok(())550 }551552 553 554 555 556 557 558 559 560 561 562 563 564 #[weight = <T as Config>::WeightInfo::add_to_white_list()]565 #[transactional]566 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{567568 let sender = ensure_signed(origin)?;569 let collection = Self::get_collection(collection_id)?;570 Self::check_owner_or_admin_permissions(&collection, sender)?;571572 <WhiteList<T>>::insert(collection_id, address, true);573 574 Ok(())575 }576577 578 579 580 581 582 583 584 585 586 587 588 589 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]590 #[transactional]591 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{592593 let sender = ensure_signed(origin)?;594 let collection = Self::get_collection(collection_id)?;595 Self::check_owner_or_admin_permissions(&collection, sender)?;596597 <WhiteList<T>>::remove(collection_id, address);598599 Ok(())600 }601602 603 604 605 606 607 608 609 610 611 612 613 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]614 #[transactional]615 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult616 {617 let sender = ensure_signed(origin)?;618619 let mut target_collection = Self::get_collection(collection_id)?;620 Self::check_owner_permissions(&target_collection, sender)?;621 target_collection.access = mode;622 Self::save_collection(target_collection);623624 Ok(())625 }626627 628 629 630 631 632 633 634 635 636 637 638 639 640 #[weight = <T as Config>::WeightInfo::set_mint_permission()]641 #[transactional]642 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult643 {644 let sender = ensure_signed(origin)?;645646 let mut target_collection = Self::get_collection(collection_id)?;647 Self::check_owner_permissions(&target_collection, sender)?;648 target_collection.mint_mode = mint_permission;649 Self::save_collection(target_collection);650651 Ok(())652 }653654 655 656 657 658 659 660 661 662 663 664 665 #[weight = <T as Config>::WeightInfo::change_collection_owner()]666 #[transactional]667 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {668669 let sender = ensure_signed(origin)?;670 let mut target_collection = Self::get_collection(collection_id)?;671 Self::check_owner_permissions(&target_collection, sender)?;672 target_collection.owner = new_owner;673 Self::save_collection(target_collection);674675 Ok(())676 }677678 679 680 681 682 683 684 685 686 687 688 689 690 691 #[weight = <T as Config>::WeightInfo::add_collection_admin()]692 #[transactional]693 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {694695 let sender = ensure_signed(origin)?;696 let collection = Self::get_collection(collection_id)?;697 Self::check_owner_or_admin_permissions(&collection, sender)?;698 let mut admin_arr = <AdminList<T>>::get(collection_id);699700 match admin_arr.binary_search(&new_admin_id) {701 Ok(_) => {},702 Err(idx) => {703 let limits = ChainLimit::get();704 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);705 admin_arr.insert(idx, new_admin_id);706 <AdminList<T>>::insert(collection_id, admin_arr);707 }708 }709 Ok(())710 }711712 713 714 715 716 717 718 719 720 721 722 723 724 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]725 #[transactional]726 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {727728 let sender = ensure_signed(origin)?;729 let collection = Self::get_collection(collection_id)?;730 Self::check_owner_or_admin_permissions(&collection, sender)?;731 let mut admin_arr = <AdminList<T>>::get(collection_id);732733 match admin_arr.binary_search(&account_id) {734 Ok(idx) => {735 admin_arr.remove(idx);736 <AdminList<T>>::insert(collection_id, admin_arr);737 },738 Err(_) => {}739 }740 Ok(())741 }742743 744 745 746 747 748 749 750 751 752 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]753 #[transactional]754 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {755756 let sender = ensure_signed(origin)?;757 let mut target_collection = Self::get_collection(collection_id)?;758 Self::check_owner_permissions(&target_collection, sender)?;759760 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);761 Self::save_collection(target_collection);762763 Ok(())764 }765766 767 768 769 770 771 772 773 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]774 #[transactional]775 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {776777 let sender = ensure_signed(origin)?;778779 let mut target_collection = Self::get_collection(collection_id)?;780 ensure!(781 target_collection.sponsorship.pending_sponsor() == Some(&sender),782 Error::<T>::ConfirmUnsetSponsorFail783 );784785 target_collection.sponsorship = SponsorshipState::Confirmed(sender);786 Self::save_collection(target_collection);787788 Ok(())789 }790791 792 793 794 795 796 797 798 799 800 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]801 #[transactional]802 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {803804 let sender = ensure_signed(origin)?;805806 let mut target_collection = Self::get_collection(collection_id)?;807 Self::check_owner_permissions(&target_collection, sender)?;808809 target_collection.sponsorship = SponsorshipState::Disabled;810 Self::save_collection(target_collection);811812 Ok(())813 }814815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838839 #[weight = <T as Config>::WeightInfo::create_item(data.len())]840 #[transactional]841 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {842 let sender = ensure_signed(origin)?;843 Self::create_item_internal(sender, collection_id, owner, data)844 }845846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()865 .map(|data| { data.len() })866 .sum())]867 #[transactional]868 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {869870 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);871 let sender = ensure_signed(origin)?;872873 let target_collection = Self::get_collection(collection_id)?;874875 Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;876877 for data in &items_data {878 Self::validate_create_item_args(&target_collection, data)?;879 }880 for data in &items_data {881 Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;882 }883884 Ok(())885 }886887 888 889 890 891 892 893 894 895 896 897 898 899 900 #[weight = <T as Config>::WeightInfo::burn_item()]901 #[transactional]902 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {903904 let sender = ensure_signed(origin)?;905906 907 let target_collection = Self::get_collection(collection_id)?;908 ensure!(909 Self::is_item_owner(sender.clone(), &target_collection, item_id) ||910 (911 target_collection.limits.owner_can_transfer &&912 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())913 ),914 Error::<T>::NoPermission915 );916917 if target_collection.access == AccessMode::WhiteList {918 Self::check_white_list(&target_collection, &sender)?;919 }920921 match target_collection.mode922 {923 CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,924 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?,925 CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?,926 _ => ()927 };928929 930 Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));931932 Ok(())933 }934935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 #[weight = <T as Config>::WeightInfo::transfer()]959 #[transactional]960 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {961 let sender = ensure_signed(origin)?;962 let collection = Self::get_collection(collection_id)?;963964 Self::transfer_internal(sender, recipient, &collection, item_id, value)965 }966967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 #[weight = <T as Config>::WeightInfo::approve()]983 #[transactional]984 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {985986 let sender = ensure_signed(origin)?;987 let target_collection = Self::get_collection(collection_id)?;988989 Self::token_exists(&target_collection, item_id)?;990991 992 let bypasses_limits = target_collection.limits.owner_can_transfer &&993 Self::is_owner_or_admin_permissions(994 &target_collection,995 sender.clone(),996 );997998 let allowance_limit = if bypasses_limits {999 None1000 } else if let Some(amount) = Self::owned_amount(1001 sender.clone(),1002 &target_collection,1003 item_id,1004 ) {1005 Some(amount)1006 } else {1007 fail!(Error::<T>::NoPermission);1008 };10091010 if target_collection.access == AccessMode::WhiteList {1011 Self::check_white_list(&target_collection, &sender)?;1012 Self::check_white_list(&target_collection, &spender)?;1013 }10141015 let allowance: u128 = amount1016 .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1017 .ok_or(Error::<T>::NumOverflow)?;1018 if let Some(limit) = allowance_limit {1019 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1020 }1021 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10221023 Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1024 Ok(())1025 }1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 #[weight = <T as Config>::WeightInfo::transfer_from()]1047 #[transactional]1048 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10491050 let sender = ensure_signed(origin)?;1051 let target_collection = Self::get_collection(collection_id)?;10521053 1054 let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10551056 1057 Self::is_correct_transfer(&target_collection, &recipient)?;10581059 1060 ensure!(1061 approval >= value || 1062 (1063 target_collection.limits.owner_can_transfer &&1064 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1065 ),1066 Error::<T>::NoPermission1067 );10681069 if target_collection.access == AccessMode::WhiteList {1070 Self::check_white_list(&target_collection, &sender)?;1071 Self::check_white_list(&target_collection, &recipient)?;1072 }10731074 1075 if approval.saturating_sub(value) > 0 {1076 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1077 }1078 else {1079 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1080 }10811082 match target_collection.mode1083 {1084 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1085 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1086 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1087 _ => ()1088 };10891090 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1091 Ok(())1092 }10931094 1095 10961097 1098 1099 1100 11011102 11031104 11051106 1107 11081109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1122 #[transactional]1123 pub fn set_variable_meta_data (1124 origin,1125 collection_id: CollectionId,1126 item_id: TokenId,1127 data: Vec<u8>1128 ) -> DispatchResult {1129 let sender = ensure_signed(origin)?;1130 1131 let target_collection = Self::get_collection(collection_id)?;1132 Self::token_exists(&target_collection, item_id)?;11331134 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11351136 1137 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1138 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1139 Error::<T>::NoPermission);11401141 match target_collection.mode1142 {1143 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1144 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1145 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1146 _ => fail!(Error::<T>::UnexpectedCollectionType)1147 };11481149 Ok(())1150 }1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 #[weight = <T as Config>::WeightInfo::set_schema_version()]1167 #[transactional]1168 pub fn set_schema_version(1169 origin,1170 collection_id: CollectionId,1171 version: SchemaVersion1172 ) -> DispatchResult {1173 let sender = ensure_signed(origin)?;1174 let mut target_collection = Self::get_collection(collection_id)?;1175 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1176 target_collection.schema_version = version;1177 Self::save_collection(target_collection);11781179 Ok(())1180 }11811182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1195 #[transactional]1196 pub fn set_offchain_schema(1197 origin,1198 collection_id: CollectionId,1199 schema: Vec<u8>1200 ) -> DispatchResult {1201 let sender = ensure_signed(origin)?;1202 let mut target_collection = Self::get_collection(collection_id)?;1203 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12041205 1206 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12071208 target_collection.offchain_schema = schema;1209 Self::save_collection(target_collection);12101211 Ok(())1212 }12131214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1227 #[transactional]1228 pub fn set_const_on_chain_schema (1229 origin,1230 collection_id: CollectionId,1231 schema: Vec<u8>1232 ) -> DispatchResult {1233 let sender = ensure_signed(origin)?;1234 let mut target_collection = Self::get_collection(collection_id)?;1235 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12361237 1238 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12391240 target_collection.const_on_chain_schema = schema;1241 Self::save_collection(target_collection);12421243 Ok(())1244 }12451246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1259 #[transactional]1260 pub fn set_variable_on_chain_schema (1261 origin,1262 collection_id: CollectionId,1263 schema: Vec<u8>1264 ) -> DispatchResult {1265 let sender = ensure_signed(origin)?;1266 let mut target_collection = Self::get_collection(collection_id)?;1267 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12681269 1270 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12711272 target_collection.variable_on_chain_schema = schema;1273 Self::save_collection(target_collection);12741275 Ok(())1276 }12771278 1279 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1280 #[transactional]1281 pub fn set_chain_limits(1282 origin,1283 limits: ChainLimits1284 ) -> DispatchResult {12851286 #[cfg(not(feature = "runtime-benchmarks"))]1287 ensure_root(origin)?;12881289 <ChainLimit>::put(limits);1290 Ok(())1291 }12921293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1305 #[transactional]1306 pub fn enable_contract_sponsoring(1307 origin,1308 contract_address: T::AccountId,1309 enable: bool1310 ) -> DispatchResult {13111312 let sender = ensure_signed(origin)?;13131314 #[cfg(feature = "runtime-benchmarks")]1315 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13161317 Self::ensure_contract_owned(sender, &contract_address)?;13181319 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1320 Ok(())1321 }13221323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1341 #[transactional]1342 pub fn set_contract_sponsoring_rate_limit(1343 origin,1344 contract_address: T::AccountId,1345 rate_limit: T::BlockNumber1346 ) -> DispatchResult {1347 let sender = ensure_signed(origin)?;13481349 #[cfg(feature = "runtime-benchmarks")]1350 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13511352 Self::ensure_contract_owned(sender, &contract_address)?;1353 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1354 Ok(())1355 }13561357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1369 #[transactional]1370 pub fn toggle_contract_white_list(1371 origin,1372 contract_address: T::AccountId,1373 enable: bool1374 ) -> DispatchResult {1375 let sender = ensure_signed(origin)?;13761377 #[cfg(feature = "runtime-benchmarks")]1378 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13791380 Self::ensure_contract_owned(sender, &contract_address)?;1381 if enable {1382 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1383 } else {1384 <ContractWhiteListEnabled<T>>::remove(contract_address);1385 }1386 Ok(())1387 }1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1401 #[transactional]1402 pub fn add_to_contract_white_list(1403 origin,1404 contract_address: T::AccountId,1405 account_address: T::AccountId1406 ) -> DispatchResult {1407 let sender = ensure_signed(origin)?;14081409 #[cfg(feature = "runtime-benchmarks")]1410 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1411 1412 Self::ensure_contract_owned(sender, &contract_address)?; 1413 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1414 Ok(())1415 }14161417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1429 #[transactional]1430 pub fn remove_from_contract_white_list(1431 origin,1432 contract_address: T::AccountId,1433 account_address: T::AccountId1434 ) -> DispatchResult {1435 let sender = ensure_signed(origin)?;14361437 #[cfg(feature = "runtime-benchmarks")]1438 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14391440 Self::ensure_contract_owned(sender, &contract_address)?;1441 <ContractWhiteList<T>>::remove(contract_address, account_address);1442 Ok(())1443 }14441445 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1446 #[transactional]1447 pub fn set_collection_limits(1448 origin,1449 collection_id: u32,1450 new_limits: CollectionLimits<T::BlockNumber>,1451 ) -> DispatchResult {1452 let sender = ensure_signed(origin)?;1453 let mut target_collection = Self::get_collection(collection_id)?;1454 Self::check_owner_permissions(&target_collection, sender.clone())?;1455 let old_limits = &target_collection.limits;1456 let chain_limits = ChainLimit::get();14571458 1459 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1460 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1461 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1462 Error::<T>::CollectionLimitBoundsExceeded);14631464 1465 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1466 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14671468 ensure!(1469 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1470 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1471 Error::<T>::OwnerPermissionsCantBeReverted,1472 );14731474 target_collection.limits = new_limits;1475 Self::save_collection(target_collection);14761477 Ok(())1478 } 1479 }1480}14811482impl<T: Config> Module<T> {1483 pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1484 let target_collection = Self::get_collection(collection_id)?;14851486 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1487 Self::validate_create_item_args(&target_collection, &data)?;1488 Self::create_item_no_validation(&target_collection, owner, data)?;14891490 Ok(())1491 }14921493 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1494 1495 Self::is_correct_transfer(target_collection, &recipient)?;14961497 1498 ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1499 Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1500 Error::<T>::NoPermission);15011502 if target_collection.access == AccessMode::WhiteList {1503 Self::check_white_list(target_collection, &sender)?;1504 Self::check_white_list(target_collection, &recipient)?;1505 }15061507 match target_collection.mode1508 {1509 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1510 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1511 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1512 _ => ()1513 };15141515 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));15161517 Ok(())1518 }151915201521 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1522 let collection_id = collection.id;15231524 1525 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1526 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1527 1528 Ok(())1529 }15301531 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1532 let collection_id = collection.id;15331534 1535 let total_items: u32 = ItemListIndex::get(collection_id)1536 .checked_add(amount)1537 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1538 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1539 .checked_add(amount)1540 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1541 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1542 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);15431544 if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1545 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1546 Self::check_white_list(collection, owner)?;1547 Self::check_white_list(collection, sender)?;1548 }15491550 Ok(())1551 }15521553 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1554 match target_collection.mode1555 {1556 CollectionMode::NFT => {1557 if let CreateItemData::NFT(data) = data {1558 1559 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1560 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1561 } else {1562 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1563 }1564 },1565 CollectionMode::Fungible(_) => {1566 if let CreateItemData::Fungible(_) = data {1567 } else {1568 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1569 }1570 },1571 CollectionMode::ReFungible => {1572 if let CreateItemData::ReFungible(data) = data {15731574 1575 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1576 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15771578 1579 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1580 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1581 } else {1582 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1583 }1584 },1585 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1586 };15871588 Ok(())1589 }15901591 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1592 match data1593 {1594 CreateItemData::NFT(data) => {1595 let item = NftItemType {1596 owner: owner.clone(),1597 const_data: data.const_data,1598 variable_data: data.variable_data1599 };16001601 Self::add_nft_item(collection, item)?;1602 },1603 CreateItemData::Fungible(data) => {1604 Self::add_fungible_item(collection, &owner, data.value)?;1605 },1606 CreateItemData::ReFungible(data) => {1607 let mut owner_list = Vec::new();1608 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16091610 let item = ReFungibleItemType {1611 owner: owner_list,1612 const_data: data.const_data,1613 variable_data: data.variable_data1614 };16151616 Self::add_refungible_item(collection, item)?;1617 }1618 };16191620 Ok(())1621 }16221623 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1624 let collection_id = collection.id;16251626 1627 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16281629 1630 let item = FungibleItemType {1631 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1632 };1633 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16341635 1636 let new_balance = <Balance<T>>::get(collection_id, owner)1637 .checked_add(value)1638 .ok_or(Error::<T>::NumOverflow)?;1639 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16401641 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1642 Ok(())1643 }16441645 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1646 let collection_id = collection.id;16471648 let current_index = <ItemListIndex>::get(collection_id)1649 .checked_add(1)1650 .ok_or(Error::<T>::NumOverflow)?;1651 let itemcopy = item.clone();16521653 ensure!(1654 item.owner.len() == 1,1655 Error::<T>::BadCreateRefungibleCall,1656 );1657 let item_owner = item.owner.first().expect("only one owner is defined");16581659 let value = item_owner.fraction;1660 let owner = item_owner.owner.clone();16611662 Self::add_token_index(collection_id, current_index, &owner)?;16631664 <ItemListIndex>::insert(collection_id, current_index);1665 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16661667 1668 let new_balance = <Balance<T>>::get(collection_id, &owner)1669 .checked_add(value)1670 .ok_or(Error::<T>::NumOverflow)?;1671 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16721673 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1674 Ok(())1675 }16761677 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1678 let collection_id = collection.id;16791680 let current_index = <ItemListIndex>::get(collection_id)1681 .checked_add(1)1682 .ok_or(Error::<T>::NumOverflow)?;16831684 let item_owner = item.owner.clone();1685 Self::add_token_index(collection_id, current_index, &item.owner)?;16861687 <ItemListIndex>::insert(collection_id, current_index);1688 <NftItemList<T>>::insert(collection_id, current_index, item);16891690 1691 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1692 .checked_add(1)1693 .ok_or(Error::<T>::NumOverflow)?;1694 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16951696 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1697 Ok(())1698 }16991700 fn burn_refungible_item(1701 collection: &CollectionHandle<T>,1702 item_id: TokenId,1703 owner: &T::AccountId,1704 ) -> DispatchResult {1705 let collection_id = collection.id;17061707 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1708 .ok_or(Error::<T>::TokenNotFound)?;1709 let rft_balance = token1710 .owner1711 .iter()1712 .find(|&i| i.owner == *owner)1713 .ok_or(Error::<T>::TokenNotFound)?;1714 Self::remove_token_index(collection_id, item_id, owner)?;17151716 1717 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1718 .checked_sub(rft_balance.fraction)1719 .ok_or(Error::<T>::NumOverflow)?;1720 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17211722 1723 let index = token1724 .owner1725 .iter()1726 .position(|i| i.owner == *owner)1727 .expect("owned item is exists");1728 token.owner.remove(index);1729 let owner_count = token.owner.len();17301731 1732 if owner_count == 0 {1733 <ReFungibleItemList<T>>::remove(collection_id, item_id);1734 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1735 }1736 else {1737 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1738 }17391740 Ok(())1741 }17421743 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1744 let collection_id = collection.id;17451746 let item = <NftItemList<T>>::get(collection_id, item_id)1747 .ok_or(Error::<T>::TokenNotFound)?;1748 Self::remove_token_index(collection_id, item_id, &item.owner)?;17491750 1751 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1752 .checked_sub(1)1753 .ok_or(Error::<T>::NumOverflow)?;1754 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1755 <NftItemList<T>>::remove(collection_id, item_id);1756 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17571758 Ok(())1759 }17601761 fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1762 let collection_id = collection.id;17631764 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1765 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17661767 1768 let new_balance = <Balance<T>>::get(collection_id, owner)1769 .checked_sub(value)1770 .ok_or(Error::<T>::NumOverflow)?;1771 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17721773 if balance.value - value > 0 {1774 balance.value -= value;1775 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1776 }1777 else {1778 <FungibleItemList<T>>::remove(collection_id, owner);1779 }17801781 Ok(())1782 }17831784 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1785 Ok(<CollectionById<T>>::get(collection_id)1786 .map(|collection| CollectionHandle {1787 id: collection_id,1788 collection1789 })1790 .ok_or(Error::<T>::CollectionNotFound)?)1791 }17921793 fn save_collection(collection: CollectionHandle<T>) {1794 <CollectionById<T>>::insert(collection.id, collection.collection);1795 }17961797 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1798 ensure!(1799 subject == target_collection.owner,1800 Error::<T>::NoPermission1801 );18021803 Ok(())1804 }18051806 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1807 subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1808 }18091810 fn check_owner_or_admin_permissions(1811 collection: &CollectionHandle<T>,1812 subject: T::AccountId,1813 ) -> DispatchResult {1814 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18151816 Ok(())1817 }18181819 fn owned_amount(1820 subject: T::AccountId,1821 target_collection: &CollectionHandle<T>,1822 item_id: TokenId,1823 ) -> Option<u128> {1824 let collection_id = target_collection.id;18251826 match target_collection.mode {1827 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1828 .then(|| 1),1829 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1830 .value),1831 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1832 .owner1833 .iter()1834 .find(|i| i.owner == subject)1835 .map(|i| i.fraction),1836 CollectionMode::Invalid => None,1837 }1838 }18391840 fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1841 match target_collection.mode {1842 CollectionMode::Fungible(_) => true,1843 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1844 }1845 }18461847 fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1848 let collection_id = collection.id;18491850 let mes = Error::<T>::AddresNotInWhiteList;1851 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18521853 Ok(())1854 }18551856 1857 1858 fn token_exists(1859 target_collection: &CollectionHandle<T>,1860 item_id: TokenId,1861 ) -> DispatchResult {1862 let collection_id = target_collection.id;1863 let exists = match target_collection.mode1864 {1865 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1866 CollectionMode::Fungible(_) => true,1867 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1868 _ => false1869 };18701871 ensure!(exists == true, Error::<T>::TokenNotFound);1872 Ok(())1873 }18741875 fn transfer_fungible(1876 collection: &CollectionHandle<T>,1877 value: u128,1878 owner: &T::AccountId,1879 recipient: &T::AccountId,1880 ) -> DispatchResult {1881 let collection_id = collection.id;18821883 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1884 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18851886 1887 Self::add_fungible_item(collection, recipient, value)?;18881889 1890 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18911892 1893 if balance.value == value {1894 <FungibleItemList<T>>::remove(collection_id, owner);1895 }1896 else {1897 balance.value -= value;1898 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1899 }19001901 Ok(())1902 }19031904 fn transfer_refungible(1905 collection: &CollectionHandle<T>,1906 item_id: TokenId,1907 value: u128,1908 owner: T::AccountId,1909 new_owner: T::AccountId,1910 ) -> DispatchResult {1911 let collection_id = collection.id;1912 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1913 .ok_or(Error::<T>::TokenNotFound)?;19141915 let item = full_item1916 .owner1917 .iter()1918 .filter(|i| i.owner == owner)1919 .next()1920 .ok_or(Error::<T>::TokenNotFound)?;1921 let amount = item.fraction;19221923 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19241925 1926 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1927 .checked_sub(value)1928 .ok_or(Error::<T>::NumOverflow)?;1929 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19301931 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1932 .checked_add(value)1933 .ok_or(Error::<T>::NumOverflow)?;1934 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19351936 let old_owner = item.owner.clone();1937 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19381939 1940 if amount == value && !new_owner_has_account {1941 1942 1943 let mut new_full_item = full_item.clone();1944 new_full_item1945 .owner1946 .iter_mut()1947 .find(|i| i.owner == owner)1948 .expect("old owner does present in refungible")1949 .owner = new_owner.clone();1950 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19511952 1953 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1954 } else {1955 let mut new_full_item = full_item.clone();1956 new_full_item1957 .owner1958 .iter_mut()1959 .find(|i| i.owner == owner)1960 .expect("old owner does present in refungible")1961 .fraction -= value;19621963 1964 if new_owner_has_account {1965 1966 new_full_item1967 .owner1968 .iter_mut()1969 .find(|i| i.owner == new_owner)1970 .expect("new owner has account")1971 .fraction += value;1972 } else {1973 1974 new_full_item.owner.push(Ownership {1975 owner: new_owner.clone(),1976 fraction: value,1977 });1978 Self::add_token_index(collection_id, item_id, &new_owner)?;1979 }19801981 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1982 }19831984 Ok(())1985 }19861987 fn transfer_nft(1988 collection: &CollectionHandle<T>,1989 item_id: TokenId,1990 sender: T::AccountId,1991 new_owner: T::AccountId,1992 ) -> DispatchResult {1993 let collection_id = collection.id;1994 let mut item = <NftItemList<T>>::get(collection_id, item_id)1995 .ok_or(Error::<T>::TokenNotFound)?;19961997 ensure!(1998 sender == item.owner,1999 Error::<T>::MustBeTokenOwner2000 );20012002 2003 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2004 .checked_sub(1)2005 .ok_or(Error::<T>::NumOverflow)?;2006 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20072008 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2009 .checked_add(1)2010 .ok_or(Error::<T>::NumOverflow)?;2011 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20122013 2014 let old_owner = item.owner.clone();2015 item.owner = new_owner.clone();2016 <NftItemList<T>>::insert(collection_id, item_id, item);20172018 2019 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20202021 Ok(())2022 }2023 2024 fn set_re_fungible_variable_data(2025 collection: &CollectionHandle<T>,2026 item_id: TokenId,2027 data: Vec<u8>2028 ) -> DispatchResult {2029 let collection_id = collection.id;2030 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2031 .ok_or(Error::<T>::TokenNotFound)?;20322033 item.variable_data = data;20342035 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20362037 Ok(())2038 }20392040 fn set_nft_variable_data(2041 collection: &CollectionHandle<T>,2042 item_id: TokenId,2043 data: Vec<u8>2044 ) -> DispatchResult {2045 let collection_id = collection.id;2046 let mut item = <NftItemList<T>>::get(collection_id, item_id)2047 .ok_or(Error::<T>::TokenNotFound)?;2048 2049 item.variable_data = data;20502051 <NftItemList<T>>::insert(collection_id, item_id, item);2052 2053 Ok(())2054 }20552056 #[allow(dead_code)]2057 fn init_collection(item: &Collection<T>) {2058 2059 assert!(2060 item.decimal_points <= MAX_DECIMAL_POINTS,2061 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2062 );2063 assert!(2064 item.name.len() <= 64,2065 "Collection name can not be longer than 63 char"2066 );2067 assert!(2068 item.name.len() <= 256,2069 "Collection description can not be longer than 255 char"2070 );2071 assert!(2072 item.token_prefix.len() <= 16,2073 "Token prefix can not be longer than 15 char"2074 );20752076 2077 let next_id = CreatedCollectionCount::get()2078 .checked_add(1)2079 .unwrap();20802081 CreatedCollectionCount::put(next_id);2082 }20832084 #[allow(dead_code)]2085 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2086 let current_index = <ItemListIndex>::get(collection_id)2087 .checked_add(1)2088 .unwrap();20892090 let item_owner = item.owner.clone();2091 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20922093 <ItemListIndex>::insert(collection_id, current_index);20942095 2096 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2097 .checked_add(1)2098 .unwrap();2099 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2100 }21012102 #[allow(dead_code)]2103 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2104 let current_index = <ItemListIndex>::get(collection_id)2105 .checked_add(1)2106 .unwrap();21072108 Self::add_token_index(collection_id, current_index, owner).unwrap();21092110 <ItemListIndex>::insert(collection_id, current_index);21112112 2113 let new_balance = <Balance<T>>::get(collection_id, owner)2114 .checked_add(item.value)2115 .unwrap();2116 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2117 }21182119 #[allow(dead_code)]2120 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2121 let current_index = <ItemListIndex>::get(collection_id)2122 .checked_add(1)2123 .unwrap();21242125 let value = item.owner.first().unwrap().fraction;2126 let owner = item.owner.first().unwrap().owner.clone();21272128 Self::add_token_index(collection_id, current_index, &owner).unwrap();21292130 <ItemListIndex>::insert(collection_id, current_index);21312132 2133 let new_balance = <Balance<T>>::get(collection_id, &owner)2134 .checked_add(value)2135 .unwrap();2136 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2137 }21382139 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2140 2141 if <AccountItemCount<T>>::contains_key(owner) {21422143 2144 let count = <AccountItemCount<T>>::get(owner);2145 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21462147 <AccountItemCount<T>>::insert(owner.clone(), count2148 .checked_add(1)2149 .ok_or(Error::<T>::NumOverflow)?);2150 }2151 else {2152 <AccountItemCount<T>>::insert(owner.clone(), 1);2153 }21542155 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2156 if list_exists {2157 let mut list = <AddressTokens<T>>::get(collection_id, owner);2158 let item_contains = list.contains(&item_index.clone());21592160 if !item_contains {2161 list.push(item_index.clone());2162 }21632164 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2165 } else {2166 let mut itm = Vec::new();2167 itm.push(item_index.clone());2168 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2169 }21702171 Ok(())2172 }21732174 fn remove_token_index(2175 collection_id: CollectionId,2176 item_index: TokenId,2177 owner: &T::AccountId,2178 ) -> DispatchResult {21792180 2181 <AccountItemCount<T>>::insert(owner.clone(), 2182 <AccountItemCount<T>>::get(owner)2183 .checked_sub(1)2184 .ok_or(Error::<T>::NumOverflow)?);218521862187 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2188 if list_exists {2189 let mut list = <AddressTokens<T>>::get(collection_id, owner);2190 let item_contains = list.contains(&item_index.clone());21912192 if item_contains {2193 list.retain(|&item| item != item_index);2194 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2195 }2196 }21972198 Ok(())2199 }22002201 fn move_token_index(2202 collection_id: CollectionId,2203 item_index: TokenId,2204 old_owner: &T::AccountId,2205 new_owner: &T::AccountId,2206 ) -> DispatchResult {2207 Self::remove_token_index(collection_id, item_index, old_owner)?;2208 Self::add_token_index(collection_id, item_index, new_owner)?;22092210 Ok(())2211 }2212 2213 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2214 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22152216 Ok(())2217 }2218}