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::*;1516pub use frame_support::{17 construct_runtime, decl_event, decl_module, decl_storage, decl_error,18 dispatch::DispatchResult,19 ensure, fail, parameter_types,20 traits::{21 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,22 Randomness, IsSubType, WithdrawReasons,23 },24 weights::{25 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},26 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,27 WeightToFeePolynomial, DispatchClass,28 },29 StorageValue,30 transactional,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use nft_data_structs::{36 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,37 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,38 CollectionId, CollectionMode, CollectionHandle, TokenId, 39 SchemaVersion, SponsorshipState, Ownership,40 NftItemType, FungibleItemType, ReFungibleItemType41};4243#[cfg(test)]44mod mock;4546#[cfg(test)]47mod tests;4849mod default_weights;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;5354pub trait WeightInfo {55 fn create_collection() -> Weight;56 fn destroy_collection() -> Weight;57 fn add_to_white_list() -> Weight;58 fn remove_from_white_list() -> Weight;59 fn set_public_access_mode() -> Weight;60 fn set_mint_permission() -> Weight;61 fn change_collection_owner() -> Weight;62 fn add_collection_admin() -> Weight;63 fn remove_collection_admin() -> Weight;64 fn set_collection_sponsor() -> Weight;65 fn confirm_sponsorship() -> Weight;66 fn remove_collection_sponsor() -> Weight;67 fn create_item(s: usize) -> Weight;68 fn burn_item() -> Weight;69 fn transfer() -> Weight;70 fn approve() -> Weight;71 fn transfer_from() -> Weight;72 fn set_offchain_schema() -> Weight;73 fn set_const_on_chain_schema() -> Weight;74 fn set_variable_on_chain_schema() -> Weight;75 fn set_variable_meta_data() -> Weight;76 fn enable_contract_sponsoring() -> Weight;77 fn set_schema_version() -> Weight;78 fn set_chain_limits() -> Weight;79 fn set_contract_sponsoring_rate_limit() -> Weight;80 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;81 fn toggle_contract_white_list() -> Weight;82 fn add_to_contract_white_list() -> Weight;83 fn remove_from_contract_white_list() -> Weight;84 fn set_collection_limits() -> Weight;85}8687decl_error! {88 89 pub enum Error for Module<T: Config> {90 91 TotalCollectionsLimitExceeded,92 93 CollectionDecimalPointLimitExceeded, 94 95 CollectionNameLimitExceeded, 96 97 CollectionDescriptionLimitExceeded, 98 99 CollectionTokenPrefixLimitExceeded,100 101 CollectionNotFound,102 103 TokenNotFound,104 105 AdminNotFound,106 107 NumOverflow, 108 109 AlreadyAdmin, 110 111 NoPermission,112 113 ConfirmUnsetSponsorFail,114 115 PublicMintingNotAllowed,116 117 MustBeTokenOwner,118 119 TokenValueTooLow,120 121 NftSizeLimitExceeded,122 123 ApproveNotFound,124 125 TokenValueNotEnough,126 127 ApproveRequired,128 129 AddresNotInWhiteList,130 131 CollectionAdminsLimitExceeded,132 133 AddressOwnershipLimitExceeded,134 135 EmptyArgument,136 137 TokenConstDataLimitExceeded,138 139 TokenVariableDataLimitExceeded,140 141 NotNftDataUsedToMintNftCollectionToken,142 143 NotFungibleDataUsedToMintFungibleCollectionToken,144 145 NotReFungibleDataUsedToMintReFungibleCollectionToken,146 147 UnexpectedCollectionType,148 149 CantStoreMetadataInFungibleTokens,150 151 CollectionTokenLimitExceeded,152 153 AccountTokenLimitExceeded,154 155 CollectionLimitBoundsExceeded,156 157 OwnerPermissionsCantBeReverted,158 159 SchemaDataLimitExceeded,160 161 WrongRefungiblePieces,162 163 BadCreateRefungibleCall,164 }165}166167168169170pub trait Config: system::Config + Sized {171 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;172173 174 type WeightInfo: WeightInfo;175176 type Currency: Currency<Self::AccountId>;177 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;178 type TreasuryAccountId: Get<Self::AccountId>;179}180181182183184185186187188189190191192193194195196197198199200201202203decl_storage! {204 trait Store for Module<T: Config> as Nft {205206 207 208 CreatedCollectionCount: u32;209 210 ChainVersion: u64;211 212 213 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;214 215216 217 pub ChainLimit get(fn chain_limit) config(): ChainLimits;218 219220 221 222 223 DestroyedCollectionCount: u32;224 225 226 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;227 228229 230 231 232 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;233 234 235 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;236 237 238 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;239 240241 242 243 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;244245 246 247 248 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;249250 251 252 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;253 254 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;255 256 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;257 258259 260 261 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;262 263264 265 266 267 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;268 269 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;270 271 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;272 273 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;274 275276 277 278 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;279 280 281 282 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;283 284 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;285 286 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;287 288 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;289 290 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 291 292 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 293 294 }295 add_extra_genesis {296 build(|config: &GenesisConfig<T>| {297 298 for (_num, _c) in &config.collection_id {299 <Module<T>>::init_collection(_c);300 }301302 for (_num, _c, _i) in &config.nft_item_id {303 <Module<T>>::init_nft_token(*_c, _i);304 }305306 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {307 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);308 }309310 for (_num, _c, _i) in &config.refungible_item_id {311 <Module<T>>::init_refungible_token(*_c, _i);312 }313 })314 }315}316317decl_event!(318 pub enum Event<T>319 where320 AccountId = <T as system::Config>::AccountId,321 {322 323 324 325 326 327 328 329 330 331 CollectionCreated(CollectionId, u8, AccountId),332333 334 335 336 337 338 339 340 341 342 ItemCreated(CollectionId, TokenId, AccountId),343344 345 346 347 348 349 350 351 ItemDestroyed(CollectionId, TokenId),352353 354 355 356 357 358 359 360 361 362 363 364 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),365366 367 368 369 370 371 372 373 374 375 Approved(CollectionId, TokenId, AccountId, AccountId, u128),376 }377);378379decl_module! {380 pub struct Module<T: Config> for enum Call 381 where 382 origin: T::Origin383 {384 fn deposit_event() = default;385 type Error = Error<T>;386387 fn on_initialize(_now: T::BlockNumber) -> Weight {388 0389 }390391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 #[weight = <T as Config>::WeightInfo::create_collection()]408 #[transactional]409 pub fn create_collection(origin,410 collection_name: Vec<u16>,411 collection_description: Vec<u16>,412 token_prefix: Vec<u8>,413 mode: CollectionMode) -> DispatchResult {414415 416 let who = ensure_signed(origin)?;417418 419 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();420 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(421 &T::TreasuryAccountId::get(),422 T::CollectionCreationPrice::get(),423 ));424 <T as Config>::Currency::settle(425 &who,426 imbalance,427 WithdrawReasons::TRANSFER,428 ExistenceRequirement::KeepAlive,429 ).map_err(|_| Error::<T>::NoPermission)?;430431 let decimal_points = match mode {432 CollectionMode::Fungible(points) => points,433 _ => 0434 };435436 let chain_limit = ChainLimit::get();437438 let created_count = CreatedCollectionCount::get();439 let destroyed_count = DestroyedCollectionCount::get();440441 442 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);443444 445 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);446 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);447 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);448 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);449450 451 let next_id = created_count452 .checked_add(1)453 .ok_or(Error::<T>::NumOverflow)?;454455 CreatedCollectionCount::put(next_id);456457 let limits = CollectionLimits {458 sponsored_data_size: chain_limit.custom_data_limit,459 ..Default::default()460 };461462 463 let new_collection = Collection {464 owner: who.clone(),465 name: collection_name,466 mode: mode.clone(),467 mint_mode: false,468 access: AccessMode::Normal,469 description: collection_description,470 decimal_points: decimal_points,471 token_prefix: token_prefix,472 offchain_schema: Vec::new(),473 schema_version: SchemaVersion::ImageURL,474 sponsorship: SponsorshipState::Disabled,475 variable_on_chain_schema: Vec::new(),476 const_on_chain_schema: Vec::new(),477 limits,478 };479480 481 <CollectionById<T>>::insert(next_id, new_collection);482483 484 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));485486 Ok(())487 }488489 490 491 492 493 494 495 496 497 498 #[weight = <T as Config>::WeightInfo::destroy_collection()]499 #[transactional]500 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {501502 let sender = ensure_signed(origin)?;503 let collection = Self::get_collection(collection_id)?;504 Self::check_owner_permissions(&collection, sender)?;505 if !collection.limits.owner_can_destroy {506 fail!(Error::<T>::NoPermission);507 }508509 <AddressTokens<T>>::remove_prefix(collection_id);510 <Allowances<T>>::remove_prefix(collection_id);511 <Balance<T>>::remove_prefix(collection_id);512 <ItemListIndex>::remove(collection_id);513 <AdminList<T>>::remove(collection_id);514 <CollectionById<T>>::remove(collection_id);515 <WhiteList<T>>::remove_prefix(collection_id);516517 <NftItemList<T>>::remove_prefix(collection_id);518 <FungibleItemList<T>>::remove_prefix(collection_id);519 <ReFungibleItemList<T>>::remove_prefix(collection_id);520521 <NftTransferBasket<T>>::remove_prefix(collection_id);522 <FungibleTransferBasket<T>>::remove_prefix(collection_id);523 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);524525 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);526527 DestroyedCollectionCount::put(DestroyedCollectionCount::get()528 .checked_add(1)529 .ok_or(Error::<T>::NumOverflow)?);530531 Ok(())532 }533534 535 536 537 538 539 540 541 542 543 544 545 546 #[weight = <T as Config>::WeightInfo::add_to_white_list()]547 #[transactional]548 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{549550 let sender = ensure_signed(origin)?;551 let collection = Self::get_collection(collection_id)?;552 Self::check_owner_or_admin_permissions(&collection, sender)?;553554 <WhiteList<T>>::insert(collection_id, address, true);555 556 Ok(())557 }558559 560 561 562 563 564 565 566 567 568 569 570 571 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]572 #[transactional]573 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{574575 let sender = ensure_signed(origin)?;576 let collection = Self::get_collection(collection_id)?;577 Self::check_owner_or_admin_permissions(&collection, sender)?;578579 <WhiteList<T>>::remove(collection_id, address);580581 Ok(())582 }583584 585 586 587 588 589 590 591 592 593 594 595 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]596 #[transactional]597 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult598 {599 let sender = ensure_signed(origin)?;600601 let mut target_collection = Self::get_collection(collection_id)?;602 Self::check_owner_permissions(&target_collection, sender)?;603 target_collection.access = mode;604 Self::save_collection(target_collection);605606 Ok(())607 }608609 610 611 612 613 614 615 616 617 618 619 620 621 622 #[weight = <T as Config>::WeightInfo::set_mint_permission()]623 #[transactional]624 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult625 {626 let sender = ensure_signed(origin)?;627628 let mut target_collection = Self::get_collection(collection_id)?;629 Self::check_owner_permissions(&target_collection, sender)?;630 target_collection.mint_mode = mint_permission;631 Self::save_collection(target_collection);632633 Ok(())634 }635636 637 638 639 640 641 642 643 644 645 646 647 #[weight = <T as Config>::WeightInfo::change_collection_owner()]648 #[transactional]649 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {650651 let sender = ensure_signed(origin)?;652 let mut target_collection = Self::get_collection(collection_id)?;653 Self::check_owner_permissions(&target_collection, sender)?;654 target_collection.owner = new_owner;655 Self::save_collection(target_collection);656657 Ok(())658 }659660 661 662 663 664 665 666 667 668 669 670 671 672 673 #[weight = <T as Config>::WeightInfo::add_collection_admin()]674 #[transactional]675 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {676677 let sender = ensure_signed(origin)?;678 let collection = Self::get_collection(collection_id)?;679 Self::check_owner_or_admin_permissions(&collection, sender)?;680 let mut admin_arr = <AdminList<T>>::get(collection_id);681682 match admin_arr.binary_search(&new_admin_id) {683 Ok(_) => {},684 Err(idx) => {685 let limits = ChainLimit::get();686 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);687 admin_arr.insert(idx, new_admin_id);688 <AdminList<T>>::insert(collection_id, admin_arr);689 }690 }691 Ok(())692 }693694 695 696 697 698 699 700 701 702 703 704 705 706 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]707 #[transactional]708 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {709710 let sender = ensure_signed(origin)?;711 let collection = Self::get_collection(collection_id)?;712 Self::check_owner_or_admin_permissions(&collection, sender)?;713 let mut admin_arr = <AdminList<T>>::get(collection_id);714715 match admin_arr.binary_search(&account_id) {716 Ok(idx) => {717 admin_arr.remove(idx);718 <AdminList<T>>::insert(collection_id, admin_arr);719 },720 Err(_) => {}721 }722 Ok(())723 }724725 726 727 728 729 730 731 732 733 734 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]735 #[transactional]736 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {737738 let sender = ensure_signed(origin)?;739 let mut target_collection = Self::get_collection(collection_id)?;740 Self::check_owner_permissions(&target_collection, sender)?;741742 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);743 Self::save_collection(target_collection);744745 Ok(())746 }747748 749 750 751 752 753 754 755 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]756 #[transactional]757 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {758759 let sender = ensure_signed(origin)?;760761 let mut target_collection = Self::get_collection(collection_id)?;762 ensure!(763 target_collection.sponsorship.pending_sponsor() == Some(&sender),764 Error::<T>::ConfirmUnsetSponsorFail765 );766767 target_collection.sponsorship = SponsorshipState::Confirmed(sender);768 Self::save_collection(target_collection);769770 Ok(())771 }772773 774 775 776 777 778 779 780 781 782 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]783 #[transactional]784 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {785786 let sender = ensure_signed(origin)?;787788 let mut target_collection = Self::get_collection(collection_id)?;789 Self::check_owner_permissions(&target_collection, sender)?;790791 target_collection.sponsorship = SponsorshipState::Disabled;792 Self::save_collection(target_collection);793794 Ok(())795 }796797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820821 #[weight = <T as Config>::WeightInfo::create_item(data.len())]822 #[transactional]823 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {824 let sender = ensure_signed(origin)?;825 Self::create_item_internal(sender, collection_id, owner, data)826 }827828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()847 .map(|data| { data.len() })848 .sum())]849 #[transactional]850 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {851852 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);853 let sender = ensure_signed(origin)?;854855 let target_collection = Self::get_collection(collection_id)?;856857 Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;858859 for data in &items_data {860 Self::validate_create_item_args(&target_collection, data)?;861 }862 for data in &items_data {863 Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;864 }865866 Ok(())867 }868869 870 871 872 873 874 875 876 877 878 879 880 881 882 #[weight = <T as Config>::WeightInfo::burn_item()]883 #[transactional]884 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {885886 let sender = ensure_signed(origin)?;887888 889 let target_collection = Self::get_collection(collection_id)?;890 ensure!(891 Self::is_item_owner(sender.clone(), &target_collection, item_id) ||892 (893 target_collection.limits.owner_can_transfer &&894 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())895 ),896 Error::<T>::NoPermission897 );898899 if target_collection.access == AccessMode::WhiteList {900 Self::check_white_list(&target_collection, &sender)?;901 }902903 match target_collection.mode904 {905 CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,906 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?,907 CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?,908 _ => ()909 };910911 912 Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));913914 Ok(())915 }916917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 #[weight = <T as Config>::WeightInfo::transfer()]941 #[transactional]942 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {943 let sender = ensure_signed(origin)?;944 let collection = Self::get_collection(collection_id)?;945946 Self::transfer_internal(sender, recipient, &collection, item_id, value)947 }948949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 #[weight = <T as Config>::WeightInfo::approve()]965 #[transactional]966 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {967968 let sender = ensure_signed(origin)?;969 let target_collection = Self::get_collection(collection_id)?;970971 Self::token_exists(&target_collection, item_id)?;972973 974 let bypasses_limits = target_collection.limits.owner_can_transfer &&975 Self::is_owner_or_admin_permissions(976 &target_collection,977 sender.clone(),978 );979980 let allowance_limit = if bypasses_limits {981 None982 } else if let Some(amount) = Self::owned_amount(983 sender.clone(),984 &target_collection,985 item_id,986 ) {987 Some(amount)988 } else {989 fail!(Error::<T>::NoPermission);990 };991992 if target_collection.access == AccessMode::WhiteList {993 Self::check_white_list(&target_collection, &sender)?;994 Self::check_white_list(&target_collection, &spender)?;995 }996997 let allowance: u128 = amount998 .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))999 .ok_or(Error::<T>::NumOverflow)?;1000 if let Some(limit) = allowance_limit {1001 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1002 }1003 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10041005 Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1006 Ok(())1007 }1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 #[weight = <T as Config>::WeightInfo::transfer_from()]1029 #[transactional]1030 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10311032 let sender = ensure_signed(origin)?;1033 let target_collection = Self::get_collection(collection_id)?;10341035 1036 let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10371038 1039 Self::is_correct_transfer(&target_collection, &recipient)?;10401041 1042 ensure!(1043 approval >= value || 1044 (1045 target_collection.limits.owner_can_transfer &&1046 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1047 ),1048 Error::<T>::NoPermission1049 );10501051 if target_collection.access == AccessMode::WhiteList {1052 Self::check_white_list(&target_collection, &sender)?;1053 Self::check_white_list(&target_collection, &recipient)?;1054 }10551056 1057 if approval.saturating_sub(value) > 0 {1058 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1059 }1060 else {1061 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1062 }10631064 match target_collection.mode1065 {1066 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1067 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1068 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1069 _ => ()1070 };10711072 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1073 Ok(())1074 }10751076 1077 10781079 1080 1081 1082 10831084 10851086 10871088 1089 10901091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1104 #[transactional]1105 pub fn set_variable_meta_data (1106 origin,1107 collection_id: CollectionId,1108 item_id: TokenId,1109 data: Vec<u8>1110 ) -> DispatchResult {1111 let sender = ensure_signed(origin)?;1112 1113 let target_collection = Self::get_collection(collection_id)?;1114 Self::token_exists(&target_collection, item_id)?;11151116 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11171118 1119 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1120 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1121 Error::<T>::NoPermission);11221123 match target_collection.mode1124 {1125 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1126 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1127 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1128 _ => fail!(Error::<T>::UnexpectedCollectionType)1129 };11301131 Ok(())1132 }1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 #[weight = <T as Config>::WeightInfo::set_schema_version()]1149 #[transactional]1150 pub fn set_schema_version(1151 origin,1152 collection_id: CollectionId,1153 version: SchemaVersion1154 ) -> DispatchResult {1155 let sender = ensure_signed(origin)?;1156 let mut target_collection = Self::get_collection(collection_id)?;1157 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1158 target_collection.schema_version = version;1159 Self::save_collection(target_collection);11601161 Ok(())1162 }11631164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1177 #[transactional]1178 pub fn set_offchain_schema(1179 origin,1180 collection_id: CollectionId,1181 schema: Vec<u8>1182 ) -> DispatchResult {1183 let sender = ensure_signed(origin)?;1184 let mut target_collection = Self::get_collection(collection_id)?;1185 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;11861187 1188 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11891190 target_collection.offchain_schema = schema;1191 Self::save_collection(target_collection);11921193 Ok(())1194 }11951196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1209 #[transactional]1210 pub fn set_const_on_chain_schema (1211 origin,1212 collection_id: CollectionId,1213 schema: Vec<u8>1214 ) -> DispatchResult {1215 let sender = ensure_signed(origin)?;1216 let mut target_collection = Self::get_collection(collection_id)?;1217 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12181219 1220 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12211222 target_collection.const_on_chain_schema = schema;1223 Self::save_collection(target_collection);12241225 Ok(())1226 }12271228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1241 #[transactional]1242 pub fn set_variable_on_chain_schema (1243 origin,1244 collection_id: CollectionId,1245 schema: Vec<u8>1246 ) -> DispatchResult {1247 let sender = ensure_signed(origin)?;1248 let mut target_collection = Self::get_collection(collection_id)?;1249 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12501251 1252 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12531254 target_collection.variable_on_chain_schema = schema;1255 Self::save_collection(target_collection);12561257 Ok(())1258 }12591260 1261 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1262 #[transactional]1263 pub fn set_chain_limits(1264 origin,1265 limits: ChainLimits1266 ) -> DispatchResult {12671268 #[cfg(not(feature = "runtime-benchmarks"))]1269 ensure_root(origin)?;12701271 <ChainLimit>::put(limits);1272 Ok(())1273 }12741275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1287 #[transactional]1288 pub fn enable_contract_sponsoring(1289 origin,1290 contract_address: T::AccountId,1291 enable: bool1292 ) -> DispatchResult {12931294 let sender = ensure_signed(origin)?;12951296 #[cfg(feature = "runtime-benchmarks")]1297 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12981299 Self::ensure_contract_owned(sender, &contract_address)?;13001301 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1302 Ok(())1303 }13041305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1323 #[transactional]1324 pub fn set_contract_sponsoring_rate_limit(1325 origin,1326 contract_address: T::AccountId,1327 rate_limit: T::BlockNumber1328 ) -> DispatchResult {1329 let sender = ensure_signed(origin)?;13301331 #[cfg(feature = "runtime-benchmarks")]1332 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13331334 Self::ensure_contract_owned(sender, &contract_address)?;1335 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1336 Ok(())1337 }13381339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1351 #[transactional]1352 pub fn toggle_contract_white_list(1353 origin,1354 contract_address: T::AccountId,1355 enable: bool1356 ) -> DispatchResult {1357 let sender = ensure_signed(origin)?;13581359 #[cfg(feature = "runtime-benchmarks")]1360 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13611362 Self::ensure_contract_owned(sender, &contract_address)?;1363 if enable {1364 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1365 } else {1366 <ContractWhiteListEnabled<T>>::remove(contract_address);1367 }1368 Ok(())1369 }1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1383 #[transactional]1384 pub fn add_to_contract_white_list(1385 origin,1386 contract_address: T::AccountId,1387 account_address: T::AccountId1388 ) -> DispatchResult {1389 let sender = ensure_signed(origin)?;13901391 #[cfg(feature = "runtime-benchmarks")]1392 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1393 1394 Self::ensure_contract_owned(sender, &contract_address)?; 1395 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1396 Ok(())1397 }13981399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1411 #[transactional]1412 pub fn remove_from_contract_white_list(1413 origin,1414 contract_address: T::AccountId,1415 account_address: T::AccountId1416 ) -> DispatchResult {1417 let sender = ensure_signed(origin)?;14181419 #[cfg(feature = "runtime-benchmarks")]1420 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14211422 Self::ensure_contract_owned(sender, &contract_address)?;1423 <ContractWhiteList<T>>::remove(contract_address, account_address);1424 Ok(())1425 }14261427 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1428 #[transactional]1429 pub fn set_collection_limits(1430 origin,1431 collection_id: u32,1432 new_limits: CollectionLimits<T::BlockNumber>,1433 ) -> DispatchResult {1434 let sender = ensure_signed(origin)?;1435 let mut target_collection = Self::get_collection(collection_id)?;1436 Self::check_owner_permissions(&target_collection, sender.clone())?;1437 let old_limits = &target_collection.limits;1438 let chain_limits = ChainLimit::get();14391440 1441 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1442 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1443 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1444 Error::<T>::CollectionLimitBoundsExceeded);14451446 1447 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1448 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14491450 ensure!(1451 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1452 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1453 Error::<T>::OwnerPermissionsCantBeReverted,1454 );14551456 target_collection.limits = new_limits;1457 Self::save_collection(target_collection);14581459 Ok(())1460 } 1461 }1462}14631464impl<T: Config> Module<T> {1465 pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1466 let target_collection = Self::get_collection(collection_id)?;14671468 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1469 Self::validate_create_item_args(&target_collection, &data)?;1470 Self::create_item_no_validation(&target_collection, owner, data)?;14711472 Ok(())1473 }14741475 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1476 1477 Self::is_correct_transfer(target_collection, &recipient)?;14781479 1480 ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1481 Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1482 Error::<T>::NoPermission);14831484 if target_collection.access == AccessMode::WhiteList {1485 Self::check_white_list(target_collection, &sender)?;1486 Self::check_white_list(target_collection, &recipient)?;1487 }14881489 match target_collection.mode1490 {1491 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1492 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1493 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1494 _ => ()1495 };14961497 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));14981499 Ok(())1500 }150115021503 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1504 let collection_id = collection.id;15051506 1507 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1508 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1509 1510 Ok(())1511 }15121513 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1514 let collection_id = collection.id;15151516 1517 let total_items: u32 = ItemListIndex::get(collection_id)1518 .checked_add(amount)1519 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1520 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1521 .checked_add(amount)1522 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1523 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1524 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);15251526 if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1527 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1528 Self::check_white_list(collection, owner)?;1529 Self::check_white_list(collection, sender)?;1530 }15311532 Ok(())1533 }15341535 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1536 match target_collection.mode1537 {1538 CollectionMode::NFT => {1539 if let CreateItemData::NFT(data) = data {1540 1541 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1542 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1543 } else {1544 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1545 }1546 },1547 CollectionMode::Fungible(_) => {1548 if let CreateItemData::Fungible(_) = data {1549 } else {1550 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1551 }1552 },1553 CollectionMode::ReFungible => {1554 if let CreateItemData::ReFungible(data) = data {15551556 1557 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1558 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15591560 1561 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1562 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1563 } else {1564 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1565 }1566 },1567 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1568 };15691570 Ok(())1571 }15721573 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1574 match data1575 {1576 CreateItemData::NFT(data) => {1577 let item = NftItemType {1578 owner: owner.clone(),1579 const_data: data.const_data,1580 variable_data: data.variable_data1581 };15821583 Self::add_nft_item(collection, item)?;1584 },1585 CreateItemData::Fungible(data) => {1586 Self::add_fungible_item(collection, &owner, data.value)?;1587 },1588 CreateItemData::ReFungible(data) => {1589 let mut owner_list = Vec::new();1590 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});15911592 let item = ReFungibleItemType {1593 owner: owner_list,1594 const_data: data.const_data,1595 variable_data: data.variable_data1596 };15971598 Self::add_refungible_item(collection, item)?;1599 }1600 };16011602 Ok(())1603 }16041605 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1606 let collection_id = collection.id;16071608 1609 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16101611 1612 let item = FungibleItemType {1613 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1614 };1615 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16161617 1618 let new_balance = <Balance<T>>::get(collection_id, owner)1619 .checked_add(value)1620 .ok_or(Error::<T>::NumOverflow)?;1621 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16221623 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1624 Ok(())1625 }16261627 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1628 let collection_id = collection.id;16291630 let current_index = <ItemListIndex>::get(collection_id)1631 .checked_add(1)1632 .ok_or(Error::<T>::NumOverflow)?;1633 let itemcopy = item.clone();16341635 ensure!(1636 item.owner.len() == 1,1637 Error::<T>::BadCreateRefungibleCall,1638 );1639 let item_owner = item.owner.first().expect("only one owner is defined");16401641 let value = item_owner.fraction;1642 let owner = item_owner.owner.clone();16431644 Self::add_token_index(collection_id, current_index, &owner)?;16451646 <ItemListIndex>::insert(collection_id, current_index);1647 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16481649 1650 let new_balance = <Balance<T>>::get(collection_id, &owner)1651 .checked_add(value)1652 .ok_or(Error::<T>::NumOverflow)?;1653 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16541655 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1656 Ok(())1657 }16581659 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1660 let collection_id = collection.id;16611662 let current_index = <ItemListIndex>::get(collection_id)1663 .checked_add(1)1664 .ok_or(Error::<T>::NumOverflow)?;16651666 let item_owner = item.owner.clone();1667 Self::add_token_index(collection_id, current_index, &item.owner)?;16681669 <ItemListIndex>::insert(collection_id, current_index);1670 <NftItemList<T>>::insert(collection_id, current_index, item);16711672 1673 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1674 .checked_add(1)1675 .ok_or(Error::<T>::NumOverflow)?;1676 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16771678 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1679 Ok(())1680 }16811682 fn burn_refungible_item(1683 collection: &CollectionHandle<T>,1684 item_id: TokenId,1685 owner: &T::AccountId,1686 ) -> DispatchResult {1687 let collection_id = collection.id;16881689 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1690 .ok_or(Error::<T>::TokenNotFound)?;1691 let rft_balance = token1692 .owner1693 .iter()1694 .find(|&i| i.owner == *owner)1695 .ok_or(Error::<T>::TokenNotFound)?;1696 Self::remove_token_index(collection_id, item_id, owner)?;16971698 1699 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1700 .checked_sub(rft_balance.fraction)1701 .ok_or(Error::<T>::NumOverflow)?;1702 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17031704 1705 let index = token1706 .owner1707 .iter()1708 .position(|i| i.owner == *owner)1709 .expect("owned item is exists");1710 token.owner.remove(index);1711 let owner_count = token.owner.len();17121713 1714 if owner_count == 0 {1715 <ReFungibleItemList<T>>::remove(collection_id, item_id);1716 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1717 }1718 else {1719 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1720 }17211722 Ok(())1723 }17241725 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1726 let collection_id = collection.id;17271728 let item = <NftItemList<T>>::get(collection_id, item_id)1729 .ok_or(Error::<T>::TokenNotFound)?;1730 Self::remove_token_index(collection_id, item_id, &item.owner)?;17311732 1733 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1734 .checked_sub(1)1735 .ok_or(Error::<T>::NumOverflow)?;1736 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1737 <NftItemList<T>>::remove(collection_id, item_id);1738 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17391740 Ok(())1741 }17421743 fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1744 let collection_id = collection.id;17451746 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1747 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17481749 1750 let new_balance = <Balance<T>>::get(collection_id, owner)1751 .checked_sub(value)1752 .ok_or(Error::<T>::NumOverflow)?;1753 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17541755 if balance.value - value > 0 {1756 balance.value -= value;1757 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1758 }1759 else {1760 <FungibleItemList<T>>::remove(collection_id, owner);1761 }17621763 Ok(())1764 }17651766 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1767 Ok(<CollectionById<T>>::get(collection_id)1768 .map(|collection| CollectionHandle {1769 id: collection_id,1770 collection1771 })1772 .ok_or(Error::<T>::CollectionNotFound)?)1773 }17741775 fn save_collection(collection: CollectionHandle<T>) {1776 <CollectionById<T>>::insert(collection.id, collection.collection);1777 }17781779 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1780 ensure!(1781 subject == target_collection.owner,1782 Error::<T>::NoPermission1783 );17841785 Ok(())1786 }17871788 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1789 subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1790 }17911792 fn check_owner_or_admin_permissions(1793 collection: &CollectionHandle<T>,1794 subject: T::AccountId,1795 ) -> DispatchResult {1796 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);17971798 Ok(())1799 }18001801 fn owned_amount(1802 subject: T::AccountId,1803 target_collection: &CollectionHandle<T>,1804 item_id: TokenId,1805 ) -> Option<u128> {1806 let collection_id = target_collection.id;18071808 match target_collection.mode {1809 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1810 .then(|| 1),1811 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1812 .value),1813 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1814 .owner1815 .iter()1816 .find(|i| i.owner == subject)1817 .map(|i| i.fraction),1818 CollectionMode::Invalid => None,1819 }1820 }18211822 fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1823 match target_collection.mode {1824 CollectionMode::Fungible(_) => true,1825 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1826 }1827 }18281829 fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1830 let collection_id = collection.id;18311832 let mes = Error::<T>::AddresNotInWhiteList;1833 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18341835 Ok(())1836 }18371838 1839 1840 fn token_exists(1841 target_collection: &CollectionHandle<T>,1842 item_id: TokenId,1843 ) -> DispatchResult {1844 let collection_id = target_collection.id;1845 let exists = match target_collection.mode1846 {1847 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1848 CollectionMode::Fungible(_) => true,1849 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1850 _ => false1851 };18521853 ensure!(exists == true, Error::<T>::TokenNotFound);1854 Ok(())1855 }18561857 fn transfer_fungible(1858 collection: &CollectionHandle<T>,1859 value: u128,1860 owner: &T::AccountId,1861 recipient: &T::AccountId,1862 ) -> DispatchResult {1863 let collection_id = collection.id;18641865 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1866 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18671868 1869 Self::add_fungible_item(collection, recipient, value)?;18701871 1872 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18731874 1875 if balance.value == value {1876 <FungibleItemList<T>>::remove(collection_id, owner);1877 }1878 else {1879 balance.value -= value;1880 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1881 }18821883 Ok(())1884 }18851886 fn transfer_refungible(1887 collection: &CollectionHandle<T>,1888 item_id: TokenId,1889 value: u128,1890 owner: T::AccountId,1891 new_owner: T::AccountId,1892 ) -> DispatchResult {1893 let collection_id = collection.id;1894 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1895 .ok_or(Error::<T>::TokenNotFound)?;18961897 let item = full_item1898 .owner1899 .iter()1900 .filter(|i| i.owner == owner)1901 .next()1902 .ok_or(Error::<T>::TokenNotFound)?;1903 let amount = item.fraction;19041905 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19061907 1908 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1909 .checked_sub(value)1910 .ok_or(Error::<T>::NumOverflow)?;1911 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19121913 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1914 .checked_add(value)1915 .ok_or(Error::<T>::NumOverflow)?;1916 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19171918 let old_owner = item.owner.clone();1919 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19201921 1922 if amount == value && !new_owner_has_account {1923 1924 1925 let mut new_full_item = full_item.clone();1926 new_full_item1927 .owner1928 .iter_mut()1929 .find(|i| i.owner == owner)1930 .expect("old owner does present in refungible")1931 .owner = new_owner.clone();1932 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19331934 1935 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1936 } else {1937 let mut new_full_item = full_item.clone();1938 new_full_item1939 .owner1940 .iter_mut()1941 .find(|i| i.owner == owner)1942 .expect("old owner does present in refungible")1943 .fraction -= value;19441945 1946 if new_owner_has_account {1947 1948 new_full_item1949 .owner1950 .iter_mut()1951 .find(|i| i.owner == new_owner)1952 .expect("new owner has account")1953 .fraction += value;1954 } else {1955 1956 new_full_item.owner.push(Ownership {1957 owner: new_owner.clone(),1958 fraction: value,1959 });1960 Self::add_token_index(collection_id, item_id, &new_owner)?;1961 }19621963 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1964 }19651966 Ok(())1967 }19681969 fn transfer_nft(1970 collection: &CollectionHandle<T>,1971 item_id: TokenId,1972 sender: T::AccountId,1973 new_owner: T::AccountId,1974 ) -> DispatchResult {1975 let collection_id = collection.id;1976 let mut item = <NftItemList<T>>::get(collection_id, item_id)1977 .ok_or(Error::<T>::TokenNotFound)?;19781979 ensure!(1980 sender == item.owner,1981 Error::<T>::MustBeTokenOwner1982 );19831984 1985 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1986 .checked_sub(1)1987 .ok_or(Error::<T>::NumOverflow)?;1988 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19891990 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1991 .checked_add(1)1992 .ok_or(Error::<T>::NumOverflow)?;1993 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19941995 1996 let old_owner = item.owner.clone();1997 item.owner = new_owner.clone();1998 <NftItemList<T>>::insert(collection_id, item_id, item);19992000 2001 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20022003 Ok(())2004 }2005 2006 fn set_re_fungible_variable_data(2007 collection: &CollectionHandle<T>,2008 item_id: TokenId,2009 data: Vec<u8>2010 ) -> DispatchResult {2011 let collection_id = collection.id;2012 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2013 .ok_or(Error::<T>::TokenNotFound)?;20142015 item.variable_data = data;20162017 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20182019 Ok(())2020 }20212022 fn set_nft_variable_data(2023 collection: &CollectionHandle<T>,2024 item_id: TokenId,2025 data: Vec<u8>2026 ) -> DispatchResult {2027 let collection_id = collection.id;2028 let mut item = <NftItemList<T>>::get(collection_id, item_id)2029 .ok_or(Error::<T>::TokenNotFound)?;2030 2031 item.variable_data = data;20322033 <NftItemList<T>>::insert(collection_id, item_id, item);2034 2035 Ok(())2036 }20372038 fn init_collection(item: &Collection<T>) {2039 2040 assert!(2041 item.decimal_points <= MAX_DECIMAL_POINTS,2042 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2043 );2044 assert!(2045 item.name.len() <= 64,2046 "Collection name can not be longer than 63 char"2047 );2048 assert!(2049 item.name.len() <= 256,2050 "Collection description can not be longer than 255 char"2051 );2052 assert!(2053 item.token_prefix.len() <= 16,2054 "Token prefix can not be longer than 15 char"2055 );20562057 2058 let next_id = CreatedCollectionCount::get()2059 .checked_add(1)2060 .unwrap();20612062 CreatedCollectionCount::put(next_id);2063 }20642065 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2066 let current_index = <ItemListIndex>::get(collection_id)2067 .checked_add(1)2068 .unwrap();20692070 let item_owner = item.owner.clone();2071 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20722073 <ItemListIndex>::insert(collection_id, current_index);20742075 2076 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2077 .checked_add(1)2078 .unwrap();2079 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2080 }20812082 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2083 let current_index = <ItemListIndex>::get(collection_id)2084 .checked_add(1)2085 .unwrap();20862087 Self::add_token_index(collection_id, current_index, owner).unwrap();20882089 <ItemListIndex>::insert(collection_id, current_index);20902091 2092 let new_balance = <Balance<T>>::get(collection_id, owner)2093 .checked_add(item.value)2094 .unwrap();2095 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2096 }20972098 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2099 let current_index = <ItemListIndex>::get(collection_id)2100 .checked_add(1)2101 .unwrap();21022103 let value = item.owner.first().unwrap().fraction;2104 let owner = item.owner.first().unwrap().owner.clone();21052106 Self::add_token_index(collection_id, current_index, &owner).unwrap();21072108 <ItemListIndex>::insert(collection_id, current_index);21092110 2111 let new_balance = <Balance<T>>::get(collection_id, &owner)2112 .checked_add(value)2113 .unwrap();2114 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2115 }21162117 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2118 2119 if <AccountItemCount<T>>::contains_key(owner) {21202121 2122 let count = <AccountItemCount<T>>::get(owner);2123 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21242125 <AccountItemCount<T>>::insert(owner.clone(), count2126 .checked_add(1)2127 .ok_or(Error::<T>::NumOverflow)?);2128 }2129 else {2130 <AccountItemCount<T>>::insert(owner.clone(), 1);2131 }21322133 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2134 if list_exists {2135 let mut list = <AddressTokens<T>>::get(collection_id, owner);2136 let item_contains = list.contains(&item_index.clone());21372138 if !item_contains {2139 list.push(item_index.clone());2140 }21412142 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2143 } else {2144 let mut itm = Vec::new();2145 itm.push(item_index.clone());2146 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2147 }21482149 Ok(())2150 }21512152 fn remove_token_index(2153 collection_id: CollectionId,2154 item_index: TokenId,2155 owner: &T::AccountId,2156 ) -> DispatchResult {21572158 2159 <AccountItemCount<T>>::insert(owner.clone(), 2160 <AccountItemCount<T>>::get(owner)2161 .checked_sub(1)2162 .ok_or(Error::<T>::NumOverflow)?);216321642165 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2166 if list_exists {2167 let mut list = <AddressTokens<T>>::get(collection_id, owner);2168 let item_contains = list.contains(&item_index.clone());21692170 if item_contains {2171 list.retain(|&item| item != item_index);2172 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2173 }2174 }21752176 Ok(())2177 }21782179 fn move_token_index(2180 collection_id: CollectionId,2181 item_index: TokenId,2182 old_owner: &T::AccountId,2183 new_owner: &T::AccountId,2184 ) -> DispatchResult {2185 Self::remove_token_index(collection_id, item_index, old_owner)?;2186 Self::add_token_index(collection_id, item_index, new_owner)?;21872188 Ok(())2189 }2190 2191 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2192 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);21932194 Ok(())2195 }2196}