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 core::ops::{Deref, DerefMut};36use nft_data_structs::{37 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,38 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,39 CollectionId, CollectionMode, TokenId, 40 SchemaVersion, SponsorshipState, Ownership,41 NftItemType, FungibleItemType, ReFungibleItemType42};4344#[cfg(test)]45mod mock;4647#[cfg(test)]48mod tests;4950mod default_weights;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;5455pub trait WeightInfo {56 fn create_collection() -> Weight;57 fn destroy_collection() -> Weight;58 fn add_to_white_list() -> Weight;59 fn remove_from_white_list() -> Weight;60 fn set_public_access_mode() -> Weight;61 fn set_mint_permission() -> Weight;62 fn change_collection_owner() -> Weight;63 fn add_collection_admin() -> Weight;64 fn remove_collection_admin() -> Weight;65 fn set_collection_sponsor() -> Weight;66 fn confirm_sponsorship() -> Weight;67 fn remove_collection_sponsor() -> Weight;68 fn create_item(s: usize) -> Weight;69 fn burn_item() -> Weight;70 fn transfer() -> Weight;71 fn approve() -> Weight;72 fn transfer_from() -> Weight;73 fn set_offchain_schema() -> Weight;74 fn set_const_on_chain_schema() -> Weight;75 fn set_variable_on_chain_schema() -> Weight;76 fn set_variable_meta_data() -> Weight;77 fn enable_contract_sponsoring() -> Weight;78 fn set_schema_version() -> Weight;79 fn set_chain_limits() -> Weight;80 fn set_contract_sponsoring_rate_limit() -> Weight;81 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;82 fn toggle_contract_white_list() -> Weight;83 fn add_to_contract_white_list() -> Weight;84 fn remove_from_contract_white_list() -> Weight;85 fn set_collection_limits() -> Weight;86}8788decl_error! {89 90 pub enum Error for Module<T: Config> {91 92 TotalCollectionsLimitExceeded,93 94 CollectionDecimalPointLimitExceeded, 95 96 CollectionNameLimitExceeded, 97 98 CollectionDescriptionLimitExceeded, 99 100 CollectionTokenPrefixLimitExceeded,101 102 CollectionNotFound,103 104 TokenNotFound,105 106 AdminNotFound,107 108 NumOverflow, 109 110 AlreadyAdmin, 111 112 NoPermission,113 114 ConfirmUnsetSponsorFail,115 116 PublicMintingNotAllowed,117 118 MustBeTokenOwner,119 120 TokenValueTooLow,121 122 NftSizeLimitExceeded,123 124 ApproveNotFound,125 126 TokenValueNotEnough,127 128 ApproveRequired,129 130 AddresNotInWhiteList,131 132 CollectionAdminsLimitExceeded,133 134 AddressOwnershipLimitExceeded,135 136 EmptyArgument,137 138 TokenConstDataLimitExceeded,139 140 TokenVariableDataLimitExceeded,141 142 NotNftDataUsedToMintNftCollectionToken,143 144 NotFungibleDataUsedToMintFungibleCollectionToken,145 146 NotReFungibleDataUsedToMintReFungibleCollectionToken,147 148 UnexpectedCollectionType,149 150 CantStoreMetadataInFungibleTokens,151 152 CollectionTokenLimitExceeded,153 154 AccountTokenLimitExceeded,155 156 CollectionLimitBoundsExceeded,157 158 OwnerPermissionsCantBeReverted,159 160 SchemaDataLimitExceeded,161 162 WrongRefungiblePieces,163 164 BadCreateRefungibleCall,165 }166}167168pub struct CollectionHandle<T: system::Config> {169 pub id: CollectionId,170 pub collection: Collection<T>,171}172173impl<T: frame_system::Config> Deref for CollectionHandle<T> {174 type Target = Collection<T>;175176 fn deref(&self) -> &Self::Target {177 &self.collection178 }179}180181impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {182 fn deref_mut(&mut self) -> &mut Self::Target {183 &mut self.collection184 }185}186187pub trait Config: system::Config + Sized {188 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;189190 191 type WeightInfo: WeightInfo;192193 type Currency: Currency<Self::AccountId>;194 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;195 type TreasuryAccountId: Get<Self::AccountId>;196}197198199200201202203204205206207208209210211212213214215216217218219220decl_storage! {221 trait Store for Module<T: Config> as Nft {222223 224 225 CreatedCollectionCount: u32;226 227 ChainVersion: u64;228 229 230 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;231 232233 234 pub ChainLimit get(fn chain_limit) config(): ChainLimits;235 236237 238 239 240 DestroyedCollectionCount: u32;241 242 243 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;244 245246 247 248 249 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;250 251 252 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;253 254 255 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;256 257258 259 260 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;261262 263 264 265 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;266267 268 269 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;270 271 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;272 273 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;274 275276 277 278 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;279 280281 282 283 284 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;285 286 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;287 288 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;289 290 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;291 292293 294 295 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;296 297 298 299 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;300 301 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;302 303 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;304 305 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;306 307 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 308 309 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 310 311 }312 add_extra_genesis {313 build(|config: &GenesisConfig<T>| {314 315 for (_num, _c) in &config.collection_id {316 <Module<T>>::init_collection(_c);317 }318319 for (_num, _c, _i) in &config.nft_item_id {320 <Module<T>>::init_nft_token(*_c, _i);321 }322323 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {324 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);325 }326327 for (_num, _c, _i) in &config.refungible_item_id {328 <Module<T>>::init_refungible_token(*_c, _i);329 }330 })331 }332}333334decl_event!(335 pub enum Event<T>336 where337 AccountId = <T as system::Config>::AccountId,338 {339 340 341 342 343 344 345 346 347 348 CollectionCreated(CollectionId, u8, AccountId),349350 351 352 353 354 355 356 357 358 359 ItemCreated(CollectionId, TokenId, AccountId),360361 362 363 364 365 366 367 368 ItemDestroyed(CollectionId, TokenId),369370 371 372 373 374 375 376 377 378 379 380 381 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),382383 384 385 386 387 388 389 390 391 392 Approved(CollectionId, TokenId, AccountId, AccountId, u128),393 }394);395396decl_module! {397 pub struct Module<T: Config> for enum Call 398 where 399 origin: T::Origin400 {401 fn deposit_event() = default;402 type Error = Error<T>;403404 fn on_initialize(_now: T::BlockNumber) -> Weight {405 0406 }407408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 #[weight = <T as Config>::WeightInfo::create_collection()]425 #[transactional]426 pub fn create_collection(origin,427 collection_name: Vec<u16>,428 collection_description: Vec<u16>,429 token_prefix: Vec<u8>,430 mode: CollectionMode) -> DispatchResult {431432 433 let who = ensure_signed(origin)?;434435 436 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();437 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(438 &T::TreasuryAccountId::get(),439 T::CollectionCreationPrice::get(),440 ));441 <T as Config>::Currency::settle(442 &who,443 imbalance,444 WithdrawReasons::TRANSFER,445 ExistenceRequirement::KeepAlive,446 ).map_err(|_| Error::<T>::NoPermission)?;447448 let decimal_points = match mode {449 CollectionMode::Fungible(points) => points,450 _ => 0451 };452453 let chain_limit = ChainLimit::get();454455 let created_count = CreatedCollectionCount::get();456 let destroyed_count = DestroyedCollectionCount::get();457458 459 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);460461 462 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);463 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);464 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);465 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);466467 468 let next_id = created_count469 .checked_add(1)470 .ok_or(Error::<T>::NumOverflow)?;471472 CreatedCollectionCount::put(next_id);473474 let limits = CollectionLimits {475 sponsored_data_size: chain_limit.custom_data_limit,476 ..Default::default()477 };478479 480 let new_collection = Collection {481 owner: who.clone(),482 name: collection_name,483 mode: mode.clone(),484 mint_mode: false,485 access: AccessMode::Normal,486 description: collection_description,487 decimal_points: decimal_points,488 token_prefix: token_prefix,489 offchain_schema: Vec::new(),490 schema_version: SchemaVersion::ImageURL,491 sponsorship: SponsorshipState::Disabled,492 variable_on_chain_schema: Vec::new(),493 const_on_chain_schema: Vec::new(),494 limits,495 };496497 498 <CollectionById<T>>::insert(next_id, new_collection);499500 501 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));502503 Ok(())504 }505506 507 508 509 510 511 512 513 514 515 #[weight = <T as Config>::WeightInfo::destroy_collection()]516 #[transactional]517 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {518519 let sender = ensure_signed(origin)?;520 let collection = Self::get_collection(collection_id)?;521 Self::check_owner_permissions(&collection, sender)?;522 if !collection.limits.owner_can_destroy {523 fail!(Error::<T>::NoPermission);524 }525526 <AddressTokens<T>>::remove_prefix(collection_id);527 <Allowances<T>>::remove_prefix(collection_id);528 <Balance<T>>::remove_prefix(collection_id);529 <ItemListIndex>::remove(collection_id);530 <AdminList<T>>::remove(collection_id);531 <CollectionById<T>>::remove(collection_id);532 <WhiteList<T>>::remove_prefix(collection_id);533534 <NftItemList<T>>::remove_prefix(collection_id);535 <FungibleItemList<T>>::remove_prefix(collection_id);536 <ReFungibleItemList<T>>::remove_prefix(collection_id);537538 <NftTransferBasket<T>>::remove_prefix(collection_id);539 <FungibleTransferBasket<T>>::remove_prefix(collection_id);540 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);541542 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);543544 DestroyedCollectionCount::put(DestroyedCollectionCount::get()545 .checked_add(1)546 .ok_or(Error::<T>::NumOverflow)?);547548 Ok(())549 }550551 552 553 554 555 556 557 558 559 560 561 562 563 #[weight = <T as Config>::WeightInfo::add_to_white_list()]564 #[transactional]565 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{566567 let sender = ensure_signed(origin)?;568 let collection = Self::get_collection(collection_id)?;569 Self::check_owner_or_admin_permissions(&collection, sender)?;570571 <WhiteList<T>>::insert(collection_id, address, true);572 573 Ok(())574 }575576 577 578 579 580 581 582 583 584 585 586 587 588 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]589 #[transactional]590 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{591592 let sender = ensure_signed(origin)?;593 let collection = Self::get_collection(collection_id)?;594 Self::check_owner_or_admin_permissions(&collection, sender)?;595596 <WhiteList<T>>::remove(collection_id, address);597598 Ok(())599 }600601 602 603 604 605 606 607 608 609 610 611 612 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]613 #[transactional]614 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult615 {616 let sender = ensure_signed(origin)?;617618 let mut target_collection = Self::get_collection(collection_id)?;619 Self::check_owner_permissions(&target_collection, sender)?;620 target_collection.access = mode;621 Self::save_collection(target_collection);622623 Ok(())624 }625626 627 628 629 630 631 632 633 634 635 636 637 638 639 #[weight = <T as Config>::WeightInfo::set_mint_permission()]640 #[transactional]641 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult642 {643 let sender = ensure_signed(origin)?;644645 let mut target_collection = Self::get_collection(collection_id)?;646 Self::check_owner_permissions(&target_collection, sender)?;647 target_collection.mint_mode = mint_permission;648 Self::save_collection(target_collection);649650 Ok(())651 }652653 654 655 656 657 658 659 660 661 662 663 664 #[weight = <T as Config>::WeightInfo::change_collection_owner()]665 #[transactional]666 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {667668 let sender = ensure_signed(origin)?;669 let mut target_collection = Self::get_collection(collection_id)?;670 Self::check_owner_permissions(&target_collection, sender)?;671 target_collection.owner = new_owner;672 Self::save_collection(target_collection);673674 Ok(())675 }676677 678 679 680 681 682 683 684 685 686 687 688 689 690 #[weight = <T as Config>::WeightInfo::add_collection_admin()]691 #[transactional]692 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {693694 let sender = ensure_signed(origin)?;695 let collection = Self::get_collection(collection_id)?;696 Self::check_owner_or_admin_permissions(&collection, sender)?;697 let mut admin_arr = <AdminList<T>>::get(collection_id);698699 match admin_arr.binary_search(&new_admin_id) {700 Ok(_) => {},701 Err(idx) => {702 let limits = ChainLimit::get();703 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);704 admin_arr.insert(idx, new_admin_id);705 <AdminList<T>>::insert(collection_id, admin_arr);706 }707 }708 Ok(())709 }710711 712 713 714 715 716 717 718 719 720 721 722 723 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]724 #[transactional]725 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {726727 let sender = ensure_signed(origin)?;728 let collection = Self::get_collection(collection_id)?;729 Self::check_owner_or_admin_permissions(&collection, sender)?;730 let mut admin_arr = <AdminList<T>>::get(collection_id);731732 match admin_arr.binary_search(&account_id) {733 Ok(idx) => {734 admin_arr.remove(idx);735 <AdminList<T>>::insert(collection_id, admin_arr);736 },737 Err(_) => {}738 }739 Ok(())740 }741742 743 744 745 746 747 748 749 750 751 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]752 #[transactional]753 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {754755 let sender = ensure_signed(origin)?;756 let mut target_collection = Self::get_collection(collection_id)?;757 Self::check_owner_permissions(&target_collection, sender)?;758759 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);760 Self::save_collection(target_collection);761762 Ok(())763 }764765 766 767 768 769 770 771 772 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]773 #[transactional]774 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {775776 let sender = ensure_signed(origin)?;777778 let mut target_collection = Self::get_collection(collection_id)?;779 ensure!(780 target_collection.sponsorship.pending_sponsor() == Some(&sender),781 Error::<T>::ConfirmUnsetSponsorFail782 );783784 target_collection.sponsorship = SponsorshipState::Confirmed(sender);785 Self::save_collection(target_collection);786787 Ok(())788 }789790 791 792 793 794 795 796 797 798 799 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]800 #[transactional]801 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {802803 let sender = ensure_signed(origin)?;804805 let mut target_collection = Self::get_collection(collection_id)?;806 Self::check_owner_permissions(&target_collection, sender)?;807808 target_collection.sponsorship = SponsorshipState::Disabled;809 Self::save_collection(target_collection);810811 Ok(())812 }813814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837838 #[weight = <T as Config>::WeightInfo::create_item(data.len())]839 #[transactional]840 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {841 let sender = ensure_signed(origin)?;842 Self::create_item_internal(sender, collection_id, owner, data)843 }844845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()864 .map(|data| { data.len() })865 .sum())]866 #[transactional]867 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {868869 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);870 let sender = ensure_signed(origin)?;871872 let target_collection = Self::get_collection(collection_id)?;873874 Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;875876 for data in &items_data {877 Self::validate_create_item_args(&target_collection, data)?;878 }879 for data in &items_data {880 Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;881 }882883 Ok(())884 }885886 887 888 889 890 891 892 893 894 895 896 897 898 899 #[weight = <T as Config>::WeightInfo::burn_item()]900 #[transactional]901 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {902903 let sender = ensure_signed(origin)?;904905 906 let target_collection = Self::get_collection(collection_id)?;907 ensure!(908 Self::is_item_owner(sender.clone(), &target_collection, item_id) ||909 (910 target_collection.limits.owner_can_transfer &&911 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())912 ),913 Error::<T>::NoPermission914 );915916 if target_collection.access == AccessMode::WhiteList {917 Self::check_white_list(&target_collection, &sender)?;918 }919920 match target_collection.mode921 {922 CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,923 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?,924 CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?,925 _ => ()926 };927928 929 Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));930931 Ok(())932 }933934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 #[weight = <T as Config>::WeightInfo::transfer()]958 #[transactional]959 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {960 let sender = ensure_signed(origin)?;961 let collection = Self::get_collection(collection_id)?;962963 Self::transfer_internal(sender, recipient, &collection, item_id, value)964 }965966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 #[weight = <T as Config>::WeightInfo::approve()]982 #[transactional]983 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {984985 let sender = ensure_signed(origin)?;986 let target_collection = Self::get_collection(collection_id)?;987988 Self::token_exists(&target_collection, item_id)?;989990 991 let bypasses_limits = target_collection.limits.owner_can_transfer &&992 Self::is_owner_or_admin_permissions(993 &target_collection,994 sender.clone(),995 );996997 let allowance_limit = if bypasses_limits {998 None999 } else if let Some(amount) = Self::owned_amount(1000 sender.clone(),1001 &target_collection,1002 item_id,1003 ) {1004 Some(amount)1005 } else {1006 fail!(Error::<T>::NoPermission);1007 };10081009 if target_collection.access == AccessMode::WhiteList {1010 Self::check_white_list(&target_collection, &sender)?;1011 Self::check_white_list(&target_collection, &spender)?;1012 }10131014 let allowance: u128 = amount1015 .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1016 .ok_or(Error::<T>::NumOverflow)?;1017 if let Some(limit) = allowance_limit {1018 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1019 }1020 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10211022 Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1023 Ok(())1024 }1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 #[weight = <T as Config>::WeightInfo::transfer_from()]1046 #[transactional]1047 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10481049 let sender = ensure_signed(origin)?;1050 let target_collection = Self::get_collection(collection_id)?;10511052 1053 let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10541055 1056 Self::is_correct_transfer(&target_collection, &recipient)?;10571058 1059 ensure!(1060 approval >= value || 1061 (1062 target_collection.limits.owner_can_transfer &&1063 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1064 ),1065 Error::<T>::NoPermission1066 );10671068 if target_collection.access == AccessMode::WhiteList {1069 Self::check_white_list(&target_collection, &sender)?;1070 Self::check_white_list(&target_collection, &recipient)?;1071 }10721073 1074 if approval.saturating_sub(value) > 0 {1075 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1076 }1077 else {1078 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1079 }10801081 match target_collection.mode1082 {1083 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1084 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1085 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1086 _ => ()1087 };10881089 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1090 Ok(())1091 }10921093 1094 10951096 1097 1098 1099 11001101 11021103 11041105 1106 11071108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1121 #[transactional]1122 pub fn set_variable_meta_data (1123 origin,1124 collection_id: CollectionId,1125 item_id: TokenId,1126 data: Vec<u8>1127 ) -> DispatchResult {1128 let sender = ensure_signed(origin)?;1129 1130 let target_collection = Self::get_collection(collection_id)?;1131 Self::token_exists(&target_collection, item_id)?;11321133 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11341135 1136 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1137 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1138 Error::<T>::NoPermission);11391140 match target_collection.mode1141 {1142 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1143 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1144 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1145 _ => fail!(Error::<T>::UnexpectedCollectionType)1146 };11471148 Ok(())1149 }1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 #[weight = <T as Config>::WeightInfo::set_schema_version()]1166 #[transactional]1167 pub fn set_schema_version(1168 origin,1169 collection_id: CollectionId,1170 version: SchemaVersion1171 ) -> DispatchResult {1172 let sender = ensure_signed(origin)?;1173 let mut target_collection = Self::get_collection(collection_id)?;1174 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1175 target_collection.schema_version = version;1176 Self::save_collection(target_collection);11771178 Ok(())1179 }11801181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1194 #[transactional]1195 pub fn set_offchain_schema(1196 origin,1197 collection_id: CollectionId,1198 schema: Vec<u8>1199 ) -> DispatchResult {1200 let sender = ensure_signed(origin)?;1201 let mut target_collection = Self::get_collection(collection_id)?;1202 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12031204 1205 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12061207 target_collection.offchain_schema = schema;1208 Self::save_collection(target_collection);12091210 Ok(())1211 }12121213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1226 #[transactional]1227 pub fn set_const_on_chain_schema (1228 origin,1229 collection_id: CollectionId,1230 schema: Vec<u8>1231 ) -> DispatchResult {1232 let sender = ensure_signed(origin)?;1233 let mut target_collection = Self::get_collection(collection_id)?;1234 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12351236 1237 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12381239 target_collection.const_on_chain_schema = schema;1240 Self::save_collection(target_collection);12411242 Ok(())1243 }12441245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1258 #[transactional]1259 pub fn set_variable_on_chain_schema (1260 origin,1261 collection_id: CollectionId,1262 schema: Vec<u8>1263 ) -> DispatchResult {1264 let sender = ensure_signed(origin)?;1265 let mut target_collection = Self::get_collection(collection_id)?;1266 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12671268 1269 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12701271 target_collection.variable_on_chain_schema = schema;1272 Self::save_collection(target_collection);12731274 Ok(())1275 }12761277 1278 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1279 #[transactional]1280 pub fn set_chain_limits(1281 origin,1282 limits: ChainLimits1283 ) -> DispatchResult {12841285 #[cfg(not(feature = "runtime-benchmarks"))]1286 ensure_root(origin)?;12871288 <ChainLimit>::put(limits);1289 Ok(())1290 }12911292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1304 #[transactional]1305 pub fn enable_contract_sponsoring(1306 origin,1307 contract_address: T::AccountId,1308 enable: bool1309 ) -> DispatchResult {13101311 let sender = ensure_signed(origin)?;13121313 #[cfg(feature = "runtime-benchmarks")]1314 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13151316 Self::ensure_contract_owned(sender, &contract_address)?;13171318 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1319 Ok(())1320 }13211322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1340 #[transactional]1341 pub fn set_contract_sponsoring_rate_limit(1342 origin,1343 contract_address: T::AccountId,1344 rate_limit: T::BlockNumber1345 ) -> DispatchResult {1346 let sender = ensure_signed(origin)?;13471348 #[cfg(feature = "runtime-benchmarks")]1349 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13501351 Self::ensure_contract_owned(sender, &contract_address)?;1352 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1353 Ok(())1354 }13551356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1368 #[transactional]1369 pub fn toggle_contract_white_list(1370 origin,1371 contract_address: T::AccountId,1372 enable: bool1373 ) -> DispatchResult {1374 let sender = ensure_signed(origin)?;13751376 #[cfg(feature = "runtime-benchmarks")]1377 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13781379 Self::ensure_contract_owned(sender, &contract_address)?;1380 if enable {1381 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1382 } else {1383 <ContractWhiteListEnabled<T>>::remove(contract_address);1384 }1385 Ok(())1386 }1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1400 #[transactional]1401 pub fn add_to_contract_white_list(1402 origin,1403 contract_address: T::AccountId,1404 account_address: T::AccountId1405 ) -> DispatchResult {1406 let sender = ensure_signed(origin)?;14071408 #[cfg(feature = "runtime-benchmarks")]1409 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1410 1411 Self::ensure_contract_owned(sender, &contract_address)?; 1412 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1413 Ok(())1414 }14151416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1428 #[transactional]1429 pub fn remove_from_contract_white_list(1430 origin,1431 contract_address: T::AccountId,1432 account_address: T::AccountId1433 ) -> DispatchResult {1434 let sender = ensure_signed(origin)?;14351436 #[cfg(feature = "runtime-benchmarks")]1437 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14381439 Self::ensure_contract_owned(sender, &contract_address)?;1440 <ContractWhiteList<T>>::remove(contract_address, account_address);1441 Ok(())1442 }14431444 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1445 #[transactional]1446 pub fn set_collection_limits(1447 origin,1448 collection_id: u32,1449 new_limits: CollectionLimits<T::BlockNumber>,1450 ) -> DispatchResult {1451 let sender = ensure_signed(origin)?;1452 let mut target_collection = Self::get_collection(collection_id)?;1453 Self::check_owner_permissions(&target_collection, sender.clone())?;1454 let old_limits = &target_collection.limits;1455 let chain_limits = ChainLimit::get();14561457 1458 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1459 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1460 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1461 Error::<T>::CollectionLimitBoundsExceeded);14621463 1464 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1465 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14661467 ensure!(1468 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1469 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1470 Error::<T>::OwnerPermissionsCantBeReverted,1471 );14721473 target_collection.limits = new_limits;1474 Self::save_collection(target_collection);14751476 Ok(())1477 } 1478 }1479}14801481impl<T: Config> Module<T> {1482 pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1483 let target_collection = Self::get_collection(collection_id)?;14841485 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1486 Self::validate_create_item_args(&target_collection, &data)?;1487 Self::create_item_no_validation(&target_collection, owner, data)?;14881489 Ok(())1490 }14911492 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1493 1494 Self::is_correct_transfer(target_collection, &recipient)?;14951496 1497 ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1498 Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1499 Error::<T>::NoPermission);15001501 if target_collection.access == AccessMode::WhiteList {1502 Self::check_white_list(target_collection, &sender)?;1503 Self::check_white_list(target_collection, &recipient)?;1504 }15051506 match target_collection.mode1507 {1508 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1509 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1510 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1511 _ => ()1512 };15131514 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));15151516 Ok(())1517 }151815191520 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1521 let collection_id = collection.id;15221523 1524 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1525 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1526 1527 Ok(())1528 }15291530 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1531 let collection_id = collection.id;15321533 1534 let total_items: u32 = ItemListIndex::get(collection_id)1535 .checked_add(amount)1536 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1537 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1538 .checked_add(amount)1539 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1540 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1541 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);15421543 if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1544 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1545 Self::check_white_list(collection, owner)?;1546 Self::check_white_list(collection, sender)?;1547 }15481549 Ok(())1550 }15511552 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1553 match target_collection.mode1554 {1555 CollectionMode::NFT => {1556 if let CreateItemData::NFT(data) = data {1557 1558 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1559 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1560 } else {1561 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1562 }1563 },1564 CollectionMode::Fungible(_) => {1565 if let CreateItemData::Fungible(_) = data {1566 } else {1567 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1568 }1569 },1570 CollectionMode::ReFungible => {1571 if let CreateItemData::ReFungible(data) = data {15721573 1574 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1575 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15761577 1578 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1579 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1580 } else {1581 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1582 }1583 },1584 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1585 };15861587 Ok(())1588 }15891590 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1591 match data1592 {1593 CreateItemData::NFT(data) => {1594 let item = NftItemType {1595 owner: owner.clone(),1596 const_data: data.const_data,1597 variable_data: data.variable_data1598 };15991600 Self::add_nft_item(collection, item)?;1601 },1602 CreateItemData::Fungible(data) => {1603 Self::add_fungible_item(collection, &owner, data.value)?;1604 },1605 CreateItemData::ReFungible(data) => {1606 let mut owner_list = Vec::new();1607 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16081609 let item = ReFungibleItemType {1610 owner: owner_list,1611 const_data: data.const_data,1612 variable_data: data.variable_data1613 };16141615 Self::add_refungible_item(collection, item)?;1616 }1617 };16181619 Ok(())1620 }16211622 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1623 let collection_id = collection.id;16241625 1626 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16271628 1629 let item = FungibleItemType {1630 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1631 };1632 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16331634 1635 let new_balance = <Balance<T>>::get(collection_id, owner)1636 .checked_add(value)1637 .ok_or(Error::<T>::NumOverflow)?;1638 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16391640 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1641 Ok(())1642 }16431644 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1645 let collection_id = collection.id;16461647 let current_index = <ItemListIndex>::get(collection_id)1648 .checked_add(1)1649 .ok_or(Error::<T>::NumOverflow)?;1650 let itemcopy = item.clone();16511652 ensure!(1653 item.owner.len() == 1,1654 Error::<T>::BadCreateRefungibleCall,1655 );1656 let item_owner = item.owner.first().expect("only one owner is defined");16571658 let value = item_owner.fraction;1659 let owner = item_owner.owner.clone();16601661 Self::add_token_index(collection_id, current_index, &owner)?;16621663 <ItemListIndex>::insert(collection_id, current_index);1664 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16651666 1667 let new_balance = <Balance<T>>::get(collection_id, &owner)1668 .checked_add(value)1669 .ok_or(Error::<T>::NumOverflow)?;1670 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16711672 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1673 Ok(())1674 }16751676 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1677 let collection_id = collection.id;16781679 let current_index = <ItemListIndex>::get(collection_id)1680 .checked_add(1)1681 .ok_or(Error::<T>::NumOverflow)?;16821683 let item_owner = item.owner.clone();1684 Self::add_token_index(collection_id, current_index, &item.owner)?;16851686 <ItemListIndex>::insert(collection_id, current_index);1687 <NftItemList<T>>::insert(collection_id, current_index, item);16881689 1690 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1691 .checked_add(1)1692 .ok_or(Error::<T>::NumOverflow)?;1693 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16941695 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1696 Ok(())1697 }16981699 fn burn_refungible_item(1700 collection: &CollectionHandle<T>,1701 item_id: TokenId,1702 owner: &T::AccountId,1703 ) -> DispatchResult {1704 let collection_id = collection.id;17051706 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1707 .ok_or(Error::<T>::TokenNotFound)?;1708 let rft_balance = token1709 .owner1710 .iter()1711 .find(|&i| i.owner == *owner)1712 .ok_or(Error::<T>::TokenNotFound)?;1713 Self::remove_token_index(collection_id, item_id, owner)?;17141715 1716 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1717 .checked_sub(rft_balance.fraction)1718 .ok_or(Error::<T>::NumOverflow)?;1719 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17201721 1722 let index = token1723 .owner1724 .iter()1725 .position(|i| i.owner == *owner)1726 .expect("owned item is exists");1727 token.owner.remove(index);1728 let owner_count = token.owner.len();17291730 1731 if owner_count == 0 {1732 <ReFungibleItemList<T>>::remove(collection_id, item_id);1733 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1734 }1735 else {1736 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1737 }17381739 Ok(())1740 }17411742 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1743 let collection_id = collection.id;17441745 let item = <NftItemList<T>>::get(collection_id, item_id)1746 .ok_or(Error::<T>::TokenNotFound)?;1747 Self::remove_token_index(collection_id, item_id, &item.owner)?;17481749 1750 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1751 .checked_sub(1)1752 .ok_or(Error::<T>::NumOverflow)?;1753 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1754 <NftItemList<T>>::remove(collection_id, item_id);1755 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17561757 Ok(())1758 }17591760 fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1761 let collection_id = collection.id;17621763 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1764 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17651766 1767 let new_balance = <Balance<T>>::get(collection_id, owner)1768 .checked_sub(value)1769 .ok_or(Error::<T>::NumOverflow)?;1770 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17711772 if balance.value - value > 0 {1773 balance.value -= value;1774 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1775 }1776 else {1777 <FungibleItemList<T>>::remove(collection_id, owner);1778 }17791780 Ok(())1781 }17821783 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1784 Ok(<CollectionById<T>>::get(collection_id)1785 .map(|collection| CollectionHandle {1786 id: collection_id,1787 collection1788 })1789 .ok_or(Error::<T>::CollectionNotFound)?)1790 }17911792 fn save_collection(collection: CollectionHandle<T>) {1793 <CollectionById<T>>::insert(collection.id, collection.collection);1794 }17951796 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1797 ensure!(1798 subject == target_collection.owner,1799 Error::<T>::NoPermission1800 );18011802 Ok(())1803 }18041805 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1806 subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1807 }18081809 fn check_owner_or_admin_permissions(1810 collection: &CollectionHandle<T>,1811 subject: T::AccountId,1812 ) -> DispatchResult {1813 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18141815 Ok(())1816 }18171818 fn owned_amount(1819 subject: T::AccountId,1820 target_collection: &CollectionHandle<T>,1821 item_id: TokenId,1822 ) -> Option<u128> {1823 let collection_id = target_collection.id;18241825 match target_collection.mode {1826 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1827 .then(|| 1),1828 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1829 .value),1830 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1831 .owner1832 .iter()1833 .find(|i| i.owner == subject)1834 .map(|i| i.fraction),1835 CollectionMode::Invalid => None,1836 }1837 }18381839 fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1840 match target_collection.mode {1841 CollectionMode::Fungible(_) => true,1842 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1843 }1844 }18451846 fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1847 let collection_id = collection.id;18481849 let mes = Error::<T>::AddresNotInWhiteList;1850 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18511852 Ok(())1853 }18541855 1856 1857 fn token_exists(1858 target_collection: &CollectionHandle<T>,1859 item_id: TokenId,1860 ) -> DispatchResult {1861 let collection_id = target_collection.id;1862 let exists = match target_collection.mode1863 {1864 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1865 CollectionMode::Fungible(_) => true,1866 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1867 _ => false1868 };18691870 ensure!(exists == true, Error::<T>::TokenNotFound);1871 Ok(())1872 }18731874 fn transfer_fungible(1875 collection: &CollectionHandle<T>,1876 value: u128,1877 owner: &T::AccountId,1878 recipient: &T::AccountId,1879 ) -> DispatchResult {1880 let collection_id = collection.id;18811882 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1883 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18841885 1886 Self::add_fungible_item(collection, recipient, value)?;18871888 1889 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18901891 1892 if balance.value == value {1893 <FungibleItemList<T>>::remove(collection_id, owner);1894 }1895 else {1896 balance.value -= value;1897 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1898 }18991900 Ok(())1901 }19021903 fn transfer_refungible(1904 collection: &CollectionHandle<T>,1905 item_id: TokenId,1906 value: u128,1907 owner: T::AccountId,1908 new_owner: T::AccountId,1909 ) -> DispatchResult {1910 let collection_id = collection.id;1911 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1912 .ok_or(Error::<T>::TokenNotFound)?;19131914 let item = full_item1915 .owner1916 .iter()1917 .filter(|i| i.owner == owner)1918 .next()1919 .ok_or(Error::<T>::TokenNotFound)?;1920 let amount = item.fraction;19211922 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19231924 1925 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1926 .checked_sub(value)1927 .ok_or(Error::<T>::NumOverflow)?;1928 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19291930 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1931 .checked_add(value)1932 .ok_or(Error::<T>::NumOverflow)?;1933 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19341935 let old_owner = item.owner.clone();1936 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19371938 1939 if amount == value && !new_owner_has_account {1940 1941 1942 let mut new_full_item = full_item.clone();1943 new_full_item1944 .owner1945 .iter_mut()1946 .find(|i| i.owner == owner)1947 .expect("old owner does present in refungible")1948 .owner = new_owner.clone();1949 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19501951 1952 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1953 } else {1954 let mut new_full_item = full_item.clone();1955 new_full_item1956 .owner1957 .iter_mut()1958 .find(|i| i.owner == owner)1959 .expect("old owner does present in refungible")1960 .fraction -= value;19611962 1963 if new_owner_has_account {1964 1965 new_full_item1966 .owner1967 .iter_mut()1968 .find(|i| i.owner == new_owner)1969 .expect("new owner has account")1970 .fraction += value;1971 } else {1972 1973 new_full_item.owner.push(Ownership {1974 owner: new_owner.clone(),1975 fraction: value,1976 });1977 Self::add_token_index(collection_id, item_id, &new_owner)?;1978 }19791980 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1981 }19821983 Ok(())1984 }19851986 fn transfer_nft(1987 collection: &CollectionHandle<T>,1988 item_id: TokenId,1989 sender: T::AccountId,1990 new_owner: T::AccountId,1991 ) -> DispatchResult {1992 let collection_id = collection.id;1993 let mut item = <NftItemList<T>>::get(collection_id, item_id)1994 .ok_or(Error::<T>::TokenNotFound)?;19951996 ensure!(1997 sender == item.owner,1998 Error::<T>::MustBeTokenOwner1999 );20002001 2002 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2003 .checked_sub(1)2004 .ok_or(Error::<T>::NumOverflow)?;2005 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20062007 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2008 .checked_add(1)2009 .ok_or(Error::<T>::NumOverflow)?;2010 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20112012 2013 let old_owner = item.owner.clone();2014 item.owner = new_owner.clone();2015 <NftItemList<T>>::insert(collection_id, item_id, item);20162017 2018 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20192020 Ok(())2021 }2022 2023 fn set_re_fungible_variable_data(2024 collection: &CollectionHandle<T>,2025 item_id: TokenId,2026 data: Vec<u8>2027 ) -> DispatchResult {2028 let collection_id = collection.id;2029 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2030 .ok_or(Error::<T>::TokenNotFound)?;20312032 item.variable_data = data;20332034 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20352036 Ok(())2037 }20382039 fn set_nft_variable_data(2040 collection: &CollectionHandle<T>,2041 item_id: TokenId,2042 data: Vec<u8>2043 ) -> DispatchResult {2044 let collection_id = collection.id;2045 let mut item = <NftItemList<T>>::get(collection_id, item_id)2046 .ok_or(Error::<T>::TokenNotFound)?;2047 2048 item.variable_data = data;20492050 <NftItemList<T>>::insert(collection_id, item_id, item);2051 2052 Ok(())2053 }20542055 #[allow(dead_code)]2056 fn init_collection(item: &Collection<T>) {2057 2058 assert!(2059 item.decimal_points <= MAX_DECIMAL_POINTS,2060 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2061 );2062 assert!(2063 item.name.len() <= 64,2064 "Collection name can not be longer than 63 char"2065 );2066 assert!(2067 item.name.len() <= 256,2068 "Collection description can not be longer than 255 char"2069 );2070 assert!(2071 item.token_prefix.len() <= 16,2072 "Token prefix can not be longer than 15 char"2073 );20742075 2076 let next_id = CreatedCollectionCount::get()2077 .checked_add(1)2078 .unwrap();20792080 CreatedCollectionCount::put(next_id);2081 }20822083 #[allow(dead_code)]2084 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2085 let current_index = <ItemListIndex>::get(collection_id)2086 .checked_add(1)2087 .unwrap();20882089 let item_owner = item.owner.clone();2090 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20912092 <ItemListIndex>::insert(collection_id, current_index);20932094 2095 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2096 .checked_add(1)2097 .unwrap();2098 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2099 }21002101 #[allow(dead_code)]2102 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2103 let current_index = <ItemListIndex>::get(collection_id)2104 .checked_add(1)2105 .unwrap();21062107 Self::add_token_index(collection_id, current_index, owner).unwrap();21082109 <ItemListIndex>::insert(collection_id, current_index);21102111 2112 let new_balance = <Balance<T>>::get(collection_id, owner)2113 .checked_add(item.value)2114 .unwrap();2115 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2116 }21172118 #[allow(dead_code)]2119 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2120 let current_index = <ItemListIndex>::get(collection_id)2121 .checked_add(1)2122 .unwrap();21232124 let value = item.owner.first().unwrap().fraction;2125 let owner = item.owner.first().unwrap().owner.clone();21262127 Self::add_token_index(collection_id, current_index, &owner).unwrap();21282129 <ItemListIndex>::insert(collection_id, current_index);21302131 2132 let new_balance = <Balance<T>>::get(collection_id, &owner)2133 .checked_add(value)2134 .unwrap();2135 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2136 }21372138 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2139 2140 if <AccountItemCount<T>>::contains_key(owner) {21412142 2143 let count = <AccountItemCount<T>>::get(owner);2144 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21452146 <AccountItemCount<T>>::insert(owner.clone(), count2147 .checked_add(1)2148 .ok_or(Error::<T>::NumOverflow)?);2149 }2150 else {2151 <AccountItemCount<T>>::insert(owner.clone(), 1);2152 }21532154 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2155 if list_exists {2156 let mut list = <AddressTokens<T>>::get(collection_id, owner);2157 let item_contains = list.contains(&item_index.clone());21582159 if !item_contains {2160 list.push(item_index.clone());2161 }21622163 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2164 } else {2165 let mut itm = Vec::new();2166 itm.push(item_index.clone());2167 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2168 }21692170 Ok(())2171 }21722173 fn remove_token_index(2174 collection_id: CollectionId,2175 item_index: TokenId,2176 owner: &T::AccountId,2177 ) -> DispatchResult {21782179 2180 <AccountItemCount<T>>::insert(owner.clone(), 2181 <AccountItemCount<T>>::get(owner)2182 .checked_sub(1)2183 .ok_or(Error::<T>::NumOverflow)?);218421852186 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2187 if list_exists {2188 let mut list = <AddressTokens<T>>::get(collection_id, owner);2189 let item_contains = list.contains(&item_index.clone());21902191 if item_contains {2192 list.retain(|&item| item != item_index);2193 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2194 }2195 }21962197 Ok(())2198 }21992200 fn move_token_index(2201 collection_id: CollectionId,2202 item_index: TokenId,2203 old_owner: &T::AccountId,2204 new_owner: &T::AccountId,2205 ) -> DispatchResult {2206 Self::remove_token_index(collection_id, item_index, old_owner)?;2207 Self::add_token_index(collection_id, item_index, new_owner)?;22082209 Ok(())2210 }2211 2212 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2213 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22142215 Ok(())2216 }2217}