123456#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::{DispatchError, sp_std::prelude::Vec};38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,44 MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,45 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,46 MetaUpdatePermission, FungibleItemType, ReFungibleItemType,47};4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;66pub mod weights;67use weights::WeightInfo;6869decl_error! {70 71 pub enum Error for Module<T: Config> {72 73 CollectionDecimalPointLimitExceeded,74 75 ConfirmUnsetSponsorFail,76 77 EmptyArgument,78 79 CollectionLimitBoundsExceeded,80 81 OwnerPermissionsCantBeReverted,82 }83}84pub trait Config:85 system::Config86 + pallet_evm_coder_substrate::Config87 + pallet_common::Config88 + pallet_nonfungible::Config89 + pallet_refungible::Config90 + pallet_fungible::Config91 + Sized92{93 94 type WeightInfo: WeightInfo;95}9697type SelfWeightOf<T> = <T as Config>::WeightInfo;9899trait WeightInfoHelpers: WeightInfo {100 fn transfer() -> Weight {101 Self::transfer_nft()102 .max(Self::transfer_fungible())103 .max(Self::transfer_refungible())104 }105 fn transfer_from() -> Weight {106 Self::transfer_from_nft()107 .max(Self::transfer_from_fungible())108 .max(Self::transfer_from_refungible())109 }110 fn approve() -> Weight {111 112 Self::approve_nft()113 }114 fn set_variable_meta_data(data: u32) -> Weight {115 116 Self::set_variable_meta_data_nft(data)117 }118 fn create_item(data: u32) -> Weight {119 Self::create_item_nft(data)120 .max(Self::create_item_fungible())121 .max(Self::create_item_refungible(data))122 }123 fn create_multiple_items(amount: u32) -> Weight {124 Self::create_multiple_items_nft(amount)125 .max(Self::create_multiple_items_fungible(amount))126 .max(Self::create_multiple_items_refungible(amount))127 }128 fn burn_item() -> Weight {129 130 Self::burn_item_nft()131 }132}133impl<T: WeightInfo> WeightInfoHelpers for T {}134135136137138139140141142143144145146147148149150151152153154155156157decl_storage! {158 trait Store for Module<T: Config> as Nft {159160 161 162 ChainVersion: u64;163 164165 166 167 168 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;169 170 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;171 172 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;173 174 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;175 176177 178 179 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;180 }181}182183decl_module! {184 pub struct Module<T: Config> for enum Call185 where186 origin: T::Origin187 {188 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;189 type Error = Error<T>;190191 fn on_initialize(_now: T::BlockNumber) -> Weight {192 0193 }194195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 #[weight = <SelfWeightOf<T>>::create_collection()]212 #[transactional]213 pub fn create_collection(origin,214 collection_name: Vec<u16>,215 collection_description: Vec<u16>,216 token_prefix: Vec<u8>,217 mode: CollectionMode) -> DispatchResult {218219 220 let who = ensure_signed(origin)?;221222 223 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();224 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(225 &T::TreasuryAccountId::get(),226 T::CollectionCreationPrice::get(),227 ));228 <T as Config>::Currency::settle(229 &who,230 imbalance,231 WithdrawReasons::TRANSFER,232 ExistenceRequirement::KeepAlive,233 ).map_err(|_| Error::<T>::NoPermission)?;234235 let decimal_points = match mode {236 CollectionMode::Fungible(points) => points,237 _ => 0238 };239240 let created_count = CreatedCollectionCount::get();241 let destroyed_count = DestroyedCollectionCount::get();242243 244 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);245246 247 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);248 ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);249 ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);250 ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);251252 253 let next_id = created_count254 .checked_add(1)255 .ok_or(Error::<T>::NumOverflow)?;256257 CreatedCollectionCount::put(next_id);258259 let limits = CollectionLimits {260 sponsored_data_size: CUSTOM_DATA_LIMIT,261 ..Default::default()262 };263264 265 let new_collection = Collection {266 owner: who.clone(),267 name: collection_name,268 mode: mode.clone(),269 mint_mode: false,270 access: AccessMode::Normal,271 description: collection_description,272 decimal_points,273 token_prefix,274 offchain_schema: Vec::new(),275 schema_version: SchemaVersion::ImageURL,276 sponsorship: SponsorshipState::Disabled,277 variable_on_chain_schema: Vec::new(),278 const_on_chain_schema: Vec::new(),279 limits,280 meta_update_permission: MetaUpdatePermission::default(),281 transfers_enabled: true,282 };283284 285 <CollectionById<T>>::insert(next_id, new_collection);286287 288 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));289290 Ok(())291 }292293 294 295 296 297 298 299 300 301 302 #[weight = <SelfWeightOf<T>>::destroy_collection()]303 #[transactional]304 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {305306 let sender = ensure_signed(origin)?;307 let collection = Self::get_collection(collection_id)?;308 Self::check_owner_permissions(&collection, &sender)?;309 if !collection.limits.owner_can_destroy {310 fail!(Error::<T>::NoPermission);311 }312313 <AddressTokens<T>>::remove_prefix(collection_id, None);314 <Allowances<T>>::remove_prefix(collection_id, None);315 <Balance<T>>::remove_prefix(collection_id, None);316 <ItemListIndex>::remove(collection_id);317 <AdminList<T>>::remove(collection_id);318 <CollectionById<T>>::remove(collection_id);319 <WhiteList<T>>::remove_prefix(collection_id, None);320321 <NftItemList<T>>::remove_prefix(collection_id, None);322 <FungibleItemList<T>>::remove_prefix(collection_id, None);323 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);324325 <NftTransferBasket<T>>::remove_prefix(collection_id, None);326 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);327 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);328329 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);330331 DestroyedCollectionCount::put(DestroyedCollectionCount::get()332 .checked_add(1)333 .ok_or(Error::<T>::NumOverflow)?);334335 Ok(())336 }337338 339 340 341 342 343 344 345 346 347 348 349 350 #[weight = <SelfWeightOf<T>>::add_to_white_list()]351 #[transactional]352 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{353354 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);355 let collection = Self::get_collection(collection_id)?;356357 Self::toggle_white_list_internal(358 &sender,359 &collection,360 &address,361 true,362 )?;363364 Ok(())365 }366367 368 369 370 371 372 373 374 375 376 377 378 379 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]380 #[transactional]381 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{382383 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);384 let collection = Self::get_collection(collection_id)?;385386 Self::toggle_white_list_internal(387 &sender,388 &collection,389 &address,390 false,391 )?;392393 Ok(())394 }395396 397 398 399 400 401 402 403 404 405 406 407 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]408 #[transactional]409 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult410 {411 let sender = ensure_signed(origin)?;412413 let mut target_collection = Self::get_collection(collection_id)?;414 Self::check_owner_permissions(&target_collection, &sender)?;415 target_collection.access = mode;416 target_collection.save()417 }418419 420 421 422 423 424 425 426 427 428 429 430 431 432 #[weight = <SelfWeightOf<T>>::set_mint_permission()]433 #[transactional]434 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult435 {436 let sender = ensure_signed(origin)?;437438 let mut target_collection = Self::get_collection(collection_id)?;439 Self::check_owner_permissions(&target_collection, &sender)?;440 target_collection.mint_mode = mint_permission;441 target_collection.save()442 }443444 445 446 447 448 449 450 451 452 453 454 455 #[weight = <SelfWeightOf<T>>::change_collection_owner()]456 #[transactional]457 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {458459 let sender = ensure_signed(origin)?;460 let mut target_collection = Self::get_collection(collection_id)?;461 Self::check_owner_permissions(&target_collection, &sender)?;462 target_collection.owner = new_owner;463 target_collection.save()464 }465466 467 468 469 470 471 472 473 474 475 476 477 478 479 #[weight = <SelfWeightOf<T>>::add_collection_admin()]480 #[transactional]481 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {482 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);483 let collection = Self::get_collection(collection_id)?;484 Self::check_owner_or_admin_permissions(&collection, &sender)?;485 let mut admin_arr = <AdminList<T>>::get(collection_id);486487 match admin_arr.binary_search(&new_admin_id) {488 Ok(_) => {},489 Err(idx) => {490 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);491 admin_arr.insert(idx, new_admin_id);492 <AdminList<T>>::insert(collection_id, admin_arr);493 }494 }495 Ok(())496 }497498 499 500 501 502 503 504 505 506 507 508 509 510 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]511 #[transactional]512 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {513 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);514 let collection = Self::get_collection(collection_id)?;515 Self::check_owner_or_admin_permissions(&collection, &sender)?;516 let mut admin_arr = <AdminList<T>>::get(collection_id);517518 if let Ok(idx) = admin_arr.binary_search(&account_id) {519 admin_arr.remove(idx);520 <AdminList<T>>::insert(collection_id, admin_arr);521 }522 Ok(())523 }524525 526 527 528 529 530 531 532 533 534 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]535 #[transactional]536 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {537 let sender = ensure_signed(origin)?;538 let mut target_collection = Self::get_collection(collection_id)?;539 Self::check_owner_permissions(&target_collection, &sender)?;540541 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);542 target_collection.save()543 }544545 546 547 548 549 550 551 552 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]553 #[transactional]554 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {555 let sender = ensure_signed(origin)?;556557 let mut target_collection = Self::get_collection(collection_id)?;558 ensure!(559 target_collection.sponsorship.pending_sponsor() == Some(&sender),560 Error::<T>::ConfirmUnsetSponsorFail561 );562563 target_collection.sponsorship = SponsorshipState::Confirmed(sender);564 target_collection.save()565 }566567 568 569 570 571 572 573 574 575 576 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]577 #[transactional]578 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {579 let sender = ensure_signed(origin)?;580581 let mut target_collection = Self::get_collection(collection_id)?;582 Self::check_owner_permissions(&target_collection, &sender)?;583584 target_collection.sponsorship = SponsorshipState::Disabled;585 target_collection.save()586 }587588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611612 #[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]613 #[transactional]614 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {615 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);616 let collection = Self::get_collection(collection_id)?;617618 Self::create_item_internal(&sender, &collection, &owner, data)?;619620 collection.submit_logs()621 }622623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 #[weight = <SelfWeightOf<T>>::create_multiple_items(items_data.len() as u32)]642 #[transactional]643 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {644645 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);646 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);647 let collection = Self::get_collection(collection_id)?;648649 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;650651 collection.submit_logs()652 }653654 655656 657 658 659 660 661 662 663 664 665 666 667 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]668 #[transactional]669 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {670671 let sender = ensure_signed(origin)?;672 let mut target_collection = Self::get_collection(collection_id)?;673674 Self::check_owner_permissions(&target_collection, &sender)?;675676 target_collection.transfers_enabled = value;677 target_collection.save()678 }679680 681 682 683 684 685 686 687 688 689 690 691 692 #[weight = <T as Config>::WeightInfo::burn_item()]693 #[transactional]694 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {695696 let sender = ensure_signed(origin)?;697 let mut target_collection = Self::get_collection(collection_id)?;698699 ensure!(700 target_collection.meta_update_permission != MetaUpdatePermission::None,701 Error::<T>::MetadataFlagFrozen702 );703 Self::check_owner_permissions(&target_collection, &sender)?;704705 target_collection.meta_update_permission = value;706707 target_collection.save()708 }709710 711 712 713 714 715 716 717 718 719 720 721 722 723 #[weight = <SelfWeightOf<T>>::burn_item()]724 #[transactional]725 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {726727 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);728 let target_collection = Self::get_collection(collection_id)?;729730 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;731732 target_collection.submit_logs()733 }734735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 #[weight = <SelfWeightOf<T>>::transfer()]759 #[transactional]760 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {761 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);762 let collection = Self::get_collection(collection_id)?;763764 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;765766 collection.submit_logs()767 }768769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 #[weight = <SelfWeightOf<T>>::approve()]785 #[transactional]786 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {787 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);788 let collection = Self::get_collection(collection_id)?;789790 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;791792 collection.submit_logs()793 }794795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 #[weight = <SelfWeightOf<T>>::transfer_from()]815 #[transactional]816 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {817 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);818 let collection = Self::get_collection(collection_id)?;819820 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;821822 collection.submit_logs()823 }824 825 826 827 828 829830 831832 833834 835 836837 838 839 840 841 842 843 844 845 846 847 848 849 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]850 #[transactional]851 pub fn set_variable_meta_data (852 origin,853 collection_id: CollectionId,854 item_id: TokenId,855 data: Vec<u8>856 ) -> DispatchResult {857 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858859 let collection = Self::get_collection(collection_id)?;860 Self::meta_update_check(&sender, &collection, item_id)?;861862 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;863864 Ok(())865 }866867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 #[weight = <SelfWeightOf<T>>::set_schema_version()]882 #[transactional]883 pub fn set_schema_version(884 origin,885 collection_id: CollectionId,886 version: SchemaVersion887 ) -> DispatchResult {888 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);889 let mut target_collection = Self::get_collection(collection_id)?;890 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;891 target_collection.schema_version = version;892 target_collection.save()893 }894895 896 897 898 899 900 901 902 903 904 905 906 907 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]908 #[transactional]909 pub fn set_offchain_schema(910 origin,911 collection_id: CollectionId,912 schema: Vec<u8>913 ) -> DispatchResult {914 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915 let mut target_collection = Self::get_collection(collection_id)?;916 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;917918 919 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");920921 target_collection.offchain_schema = schema;922 target_collection.save()923 }924925 926 927 928 929 930 931 932 933 934 935 936 937 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]938 #[transactional]939 pub fn set_const_on_chain_schema (940 origin,941 collection_id: CollectionId,942 schema: Vec<u8>943 ) -> DispatchResult {944 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);945 let mut target_collection = Self::get_collection(collection_id)?;946 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;947948 949 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");950951 target_collection.const_on_chain_schema = schema;952 target_collection.save()953 }954955 956 957 958 959 960 961 962 963 964 965 966 967 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]968 #[transactional]969 pub fn set_variable_on_chain_schema (970 origin,971 collection_id: CollectionId,972 schema: Vec<u8>973 ) -> DispatchResult {974 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);975 let mut target_collection = Self::get_collection(collection_id)?;976 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;977978 979 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");980981 target_collection.variable_on_chain_schema = schema;982 target_collection.save()983 }984985 #[weight = <SelfWeightOf<T>>::set_collection_limits()]986 #[transactional]987 pub fn set_collection_limits(988 origin,989 collection_id: u32,990 new_limits: CollectionLimits<T::BlockNumber>,991 ) -> DispatchResult {992 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);993 let mut target_collection = Self::get_collection(collection_id)?;994 Self::check_owner_permissions(&target_collection, sender.as_sub())?;995 let old_limits = &target_collection.limits;996997 998 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&999 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1000 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1001 Error::<T>::CollectionLimitBoundsExceeded);10021003 1004 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1005 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);10061007 ensure!(1008 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1009 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1010 Error::<T>::OwnerPermissionsCantBeReverted,1011 );10121013 target_collection.limits = new_limits;10141015 target_collection.save()1016 }1017 }1018}10191020impl<T: Config> Module<T> {1021 pub fn create_item_internal(1022 sender: &T::CrossAccountId,1023 collection: &CollectionHandle<T>,1024 owner: &T::CrossAccountId,1025 data: CreateItemData,1026 ) -> DispatchResult {1027 ensure!(1028 owner != &T::CrossAccountId::from_eth(H160([0; 20])),1029 Error::<T>::AddressIsZero1030 );10311032 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1033 Self::validate_create_item_args(collection, &data)?;1034 Self::create_item_no_validation(collection, owner, data)?;10351036 Ok(())1037 }10381039 pub fn transfer_internal(1040 sender: &T::CrossAccountId,1041 recipient: &T::CrossAccountId,1042 target_collection: &CollectionHandle<T>,1043 item_id: TokenId,1044 value: u128,1045 ) -> DispatchResult {1046 ensure!(1047 recipient != &T::CrossAccountId::from_eth(H160([0; 20])),1048 Error::<T>::AddressIsZero1049 );10501051 1052 Self::is_correct_transfer(target_collection, recipient)?;10531054 1055 ensure!(1056 Self::is_item_owner(sender, target_collection, item_id)?1057 || Self::is_owner_or_admin_permissions(target_collection, sender)?,1058 Error::<T>::NoPermission1059 );10601061 if target_collection.access == AccessMode::WhiteList {1062 Self::check_white_list(target_collection, sender)?;1063 Self::check_white_list(target_collection, recipient)?;1064 }10651066 match target_collection.mode {1067 CollectionMode::NFT => Self::transfer_nft(1068 target_collection,1069 item_id,1070 sender.clone(),1071 recipient.clone(),1072 )?,1073 CollectionMode::Fungible(_) => {1074 Self::transfer_fungible(target_collection, value, sender, recipient)?1075 }1076 CollectionMode::ReFungible => Self::transfer_refungible(1077 target_collection,1078 item_id,1079 value,1080 sender.clone(),1081 recipient.clone(),1082 )?,1083 };10841085 Self::deposit_event(RawEvent::Transfer(1086 target_collection.id,1087 item_id,1088 sender.clone(),1089 recipient.clone(),1090 value,1091 ));10921093 Ok(())1094 }10951096 pub fn approve_internal(1097 sender: &T::CrossAccountId,1098 spender: &T::CrossAccountId,1099 collection: &CollectionHandle<T>,1100 item_id: TokenId,1101 amount: u128,1102 ) -> DispatchResult {1103 Self::token_exists(collection, item_id)?;11041105 1106 let bypasses_limits = collection.limits.owner_can_transfer1107 && Self::is_owner_or_admin_permissions(collection, sender)?;11081109 let allowance_limit = if bypasses_limits {1110 None1111 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id)? {1112 Some(amount)1113 } else {1114 fail!(Error::<T>::NoPermission);1115 };11161117 if collection.access == AccessMode::WhiteList {1118 Self::check_white_list(collection, sender)?;1119 Self::check_white_list(collection, spender)?;1120 }11211122 collection.consume_sload()?;1123 let allowance: u128 = amount1124 .checked_add(<Allowances<T>>::get(1125 collection.id,1126 (item_id, sender.as_sub(), spender.as_sub()),1127 ))1128 .ok_or(Error::<T>::NumOverflow)?;1129 if let Some(limit) = allowance_limit {1130 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1131 }1132 collection.consume_sstore()?;1133 <Allowances<T>>::insert(1134 collection.id,1135 (item_id, sender.as_sub(), spender.as_sub()),1136 allowance,1137 );11381139 if matches!(collection.mode, CollectionMode::NFT) {1140 1141 collection.log(ERC721Events::Approval {1142 owner: *sender.as_eth(),1143 approved: *spender.as_eth(),1144 token_id: item_id.into(),1145 })?;1146 }11471148 if matches!(collection.mode, CollectionMode::Fungible(_)) {1149 1150 collection.log(ERC20Events::Approval {1151 owner: *sender.as_eth(),1152 spender: *spender.as_eth(),1153 value: allowance.into(),1154 })?;1155 }11561157 Self::deposit_event(RawEvent::Approved(1158 collection.id,1159 item_id,1160 sender.clone(),1161 spender.clone(),1162 allowance,1163 ));1164 Ok(())1165 }11661167 pub fn transfer_from_internal(1168 sender: &T::CrossAccountId,1169 from: &T::CrossAccountId,1170 recipient: &T::CrossAccountId,1171 collection: &CollectionHandle<T>,1172 item_id: TokenId,1173 amount: u128,1174 ) -> DispatchResult {1175 if sender == from {1176 1177 return Self::transfer_internal(from, recipient, collection, item_id, amount);1178 }11791180 1181 collection.consume_sload()?;1182 let approval: u128 =1183 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));11841185 1186 Self::is_correct_transfer(collection, recipient)?;11871188 1189 ensure!(1190 approval >= amount1191 || (collection.limits.owner_can_transfer1192 && Self::is_owner_or_admin_permissions(collection, sender)?),1193 Error::<T>::NoPermission1194 );11951196 if collection.access == AccessMode::WhiteList {1197 Self::check_white_list(collection, sender)?;1198 Self::check_white_list(collection, recipient)?;1199 }12001201 1202 let allowance = approval.saturating_sub(amount);1203 collection.consume_sstore()?;1204 if allowance > 0 {1205 <Allowances<T>>::insert(1206 collection.id,1207 (item_id, from.as_sub(), sender.as_sub()),1208 allowance,1209 );1210 } else {1211 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1212 }12131214 match collection.mode {1215 CollectionMode::NFT => {1216 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1217 }1218 CollectionMode::Fungible(_) => {1219 Self::transfer_fungible(collection, amount, from, recipient)?1220 }1221 CollectionMode::ReFungible => Self::transfer_refungible(1222 collection,1223 item_id,1224 amount,1225 from.clone(),1226 recipient.clone(),1227 )?,1228 };12291230 if matches!(collection.mode, CollectionMode::Fungible(_)) {1231 collection.log(ERC20Events::Approval {1232 owner: *from.as_eth(),1233 spender: *sender.as_eth(),1234 value: allowance.into(),1235 })?;1236 }12371238 Ok(())1239 }12401241 pub fn set_variable_meta_data_internal(1242 sender: &T::CrossAccountId,1243 collection: &CollectionHandle<T>,1244 item_id: TokenId,1245 data: Vec<u8>,1246 ) -> DispatchResult {1247 Self::token_exists(collection, item_id)?;12481249 ensure!(1250 CUSTOM_DATA_LIMIT >= data.len() as u32,1251 Error::<T>::TokenVariableDataLimitExceeded1252 );12531254 ensure!(1255 (Self::is_item_owner(sender, collection, item_id)?1256 && collection.meta_update_permission == MetaUpdatePermission::ItemOwner)1257 || (Self::is_owner_or_admin_permissions(collection, sender)?1258 && collection.meta_update_permission == MetaUpdatePermission::Admin),1259 Error::<T>::NoPermission1260 );12611262 match collection.mode {1263 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1264 CollectionMode::ReFungible => {1265 Self::set_re_fungible_variable_data(collection, item_id, data)?1266 }1267 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1268 };12691270 Ok(())1271 }12721273 pub fn meta_update_check(1274 sender: &T::CrossAccountId,1275 collection: &CollectionHandle<T>,1276 item_id: TokenId,1277 ) -> DispatchResult {1278 match collection.meta_update_permission {1279 MetaUpdatePermission::ItemOwner => ensure!(1280 Self::is_item_owner(sender, collection, item_id)?,1281 Error::<T>::NoPermission1282 ),1283 MetaUpdatePermission::Admin => ensure!(1284 Self::is_owner_or_admin_permissions(collection, sender)?,1285 Error::<T>::NoPermission1286 ),1287 MetaUpdatePermission::None => fail!(Error::<T>::MetadataUpdateDenied),1288 }12891290 Ok(())1291 }12921293 pub fn get_variable_metadata(1294 collection: &CollectionHandle<T>,1295 item_id: TokenId,1296 ) -> Result<Vec<u8>, DispatchError> {1297 Ok(match collection.mode {1298 CollectionMode::NFT => {1299 <NftItemList<T>>::get(collection.id, item_id)1300 .ok_or(Error::<T>::TokenNotFound)?1301 .variable_data1302 }1303 CollectionMode::ReFungible => {1304 <ReFungibleItemList<T>>::get(collection.id, item_id)1305 .ok_or(Error::<T>::TokenNotFound)?1306 .variable_data1307 }1308 _ => fail!(Error::<T>::UnexpectedCollectionType),1309 })1310 }13111312 pub fn create_multiple_items_internal(1313 sender: &T::CrossAccountId,1314 collection: &CollectionHandle<T>,1315 owner: &T::CrossAccountId,1316 items_data: Vec<CreateItemData>,1317 ) -> DispatchResult {1318 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;13191320 for data in &items_data {1321 Self::validate_create_item_args(collection, data)?;1322 }1323 for data in &items_data {1324 Self::create_item_no_validation(collection, owner, data.clone())?;1325 }13261327 Ok(())1328 }13291330 pub fn burn_item_internal(1331 sender: &T::CrossAccountId,1332 collection: &CollectionHandle<T>,1333 item_id: TokenId,1334 value: u128,1335 ) -> DispatchResult {1336 ensure!(1337 Self::is_item_owner(sender, collection, item_id)?1338 || (collection.limits.owner_can_transfer1339 && Self::is_owner_or_admin_permissions(collection, sender)?),1340 Error::<T>::NoPermission1341 );13421343 if collection.access == AccessMode::WhiteList {1344 Self::check_white_list(collection, sender)?;1345 }13461347 match collection.mode {1348 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1349 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1350 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1351 };13521353 Ok(())1354 }13551356 pub fn toggle_white_list_internal(1357 sender: &T::CrossAccountId,1358 collection: &CollectionHandle<T>,1359 address: &T::CrossAccountId,1360 whitelisted: bool,1361 ) -> DispatchResult {1362 Self::check_owner_or_admin_permissions(collection, sender)?;13631364 if whitelisted {1365 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1366 } else {1367 <WhiteList<T>>::remove(collection.id, address.as_sub());1368 }13691370 Ok(())1371 }13721373 fn is_correct_transfer(1374 collection: &CollectionHandle<T>,1375 recipient: &T::CrossAccountId,1376 ) -> DispatchResult {1377 let collection_id = collection.id;13781379 1380 collection.consume_sload()?;1381 let account_items: u32 =1382 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;13831384 1385 1386 let limit = if collection.limits.account_token_ownership_limit == 01387 || collection.limits.account_token_ownership_limit > ACCOUNT_TOKEN_OWNERSHIP_LIMIT1388 {1389 ACCOUNT_TOKEN_OWNERSHIP_LIMIT1390 } else {1391 collection.limits.account_token_ownership_limit1392 };13931394 ensure!(limit > account_items, Error::<T>::AccountTokenLimitExceeded);13951396 1397 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);13981399 Ok(())1400 }14011402 fn can_create_items_in_collection(1403 collection: &CollectionHandle<T>,1404 sender: &T::CrossAccountId,1405 owner: &T::CrossAccountId,1406 amount: u32,1407 ) -> DispatchResult {1408 let collection_id = collection.id;14091410 1411 let total_items: u32 = ItemListIndex::get(collection_id)1412 .checked_add(amount)1413 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1414 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1415 as u32)1416 .checked_add(amount)1417 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;14181419 1420 1421 let account_token_limit = if collection.limits.account_token_ownership_limit == 01422 || collection.limits.account_token_ownership_limit > ACCOUNT_TOKEN_OWNERSHIP_LIMIT1423 {1424 ACCOUNT_TOKEN_OWNERSHIP_LIMIT1425 } else {1426 collection.limits.account_token_ownership_limit1427 };14281429 ensure!(1430 collection.limits.token_limit >= total_items,1431 Error::<T>::CollectionTokenLimitExceeded1432 );1433 ensure!(1434 account_token_limit >= account_items,1435 Error::<T>::AccountTokenLimitExceeded1436 );14371438 if !Self::is_owner_or_admin_permissions(collection, sender)? {1439 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1440 Self::check_white_list(collection, owner)?;1441 Self::check_white_list(collection, sender)?;1442 }14431444 Ok(())1445 }14461447 fn validate_create_item_args(1448 target_collection: &CollectionHandle<T>,1449 data: &CreateItemData,1450 ) -> DispatchResult {1451 match target_collection.mode {1452 CollectionMode::NFT => {1453 if !matches!(data, CreateItemData::NFT(_)) {1454 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1455 }1456 }1457 CollectionMode::Fungible(_) => {1458 if !matches!(data, CreateItemData::Fungible(_)) {1459 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1460 }1461 }1462 CollectionMode::ReFungible => {1463 if let CreateItemData::ReFungible(data) = data {1464 1465 ensure!(1466 data.pieces <= MAX_REFUNGIBLE_PIECES,1467 Error::<T>::WrongRefungiblePieces1468 );1469 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1470 } else {1471 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1472 }1473 }1474 };14751476 Ok(())1477 }14781479 fn create_item_no_validation(1480 collection: &CollectionHandle<T>,1481 owner: &T::CrossAccountId,1482 data: CreateItemData,1483 ) -> DispatchResult {1484 match data {1485 CreateItemData::NFT(data) => {1486 let item = NftItemType {1487 owner: owner.clone(),1488 const_data: data.const_data.into_inner(),1489 variable_data: data.variable_data.into_inner(),1490 };14911492 Self::add_nft_item(collection, item)?;1493 }1494 CreateItemData::Fungible(data) => {1495 Self::add_fungible_item(collection, owner, data.value)?;1496 }1497 CreateItemData::ReFungible(data) => {1498 let owner_list = vec![Ownership {1499 owner: owner.clone(),1500 fraction: data.pieces,1501 }];15021503 let item = ReFungibleItemType {1504 owner: owner_list,1505 const_data: data.const_data.into_inner(),1506 variable_data: data.variable_data.into_inner(),1507 };15081509 Self::add_refungible_item(collection, item)?;1510 }1511 };15121513 Ok(())1514 }15151516 fn add_fungible_item(1517 collection: &CollectionHandle<T>,1518 owner: &T::CrossAccountId,1519 value: u128,1520 ) -> DispatchResult {1521 let collection_id = collection.id;15221523 1524 collection.consume_sload()?;1525 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;15261527 1528 let item = FungibleItemType {1529 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1530 };1531 collection.consume_sstore()?;1532 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);15331534 1535 collection.consume_sload()?;1536 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1537 .checked_add(value)1538 .ok_or(Error::<T>::NumOverflow)?;1539 collection.consume_sstore()?;1540 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);15411542 collection.log(ERC20Events::Transfer {1543 from: H160::default(),1544 to: *owner.as_eth(),1545 value: value.into(),1546 })?;1547 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1548 Ok(())1549 }15501551 fn add_refungible_item(1552 collection: &CollectionHandle<T>,1553 item: ReFungibleItemType<T::CrossAccountId>,1554 ) -> DispatchResult {1555 let collection_id = collection.id;15561557 let current_index = <ItemListIndex>::get(collection_id)1558 .checked_add(1)1559 .ok_or(Error::<T>::NumOverflow)?;1560 let itemcopy = item.clone();15611562 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1563 let item_owner = item.owner.first().expect("only one owner is defined");15641565 let value = item_owner.fraction;1566 let owner = item_owner.owner.clone();15671568 Self::add_token_index(collection, current_index, &owner)?;15691570 <ItemListIndex>::insert(collection_id, current_index);1571 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);15721573 1574 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1575 .checked_add(value)1576 .ok_or(Error::<T>::NumOverflow)?;1577 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);15781579 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1580 Ok(())1581 }15821583 fn add_nft_item(1584 collection: &CollectionHandle<T>,1585 item: NftItemType<T::CrossAccountId>,1586 ) -> DispatchResult {1587 let collection_id = collection.id;15881589 let current_index = <ItemListIndex>::get(collection_id)1590 .checked_add(1)1591 .ok_or(Error::<T>::NumOverflow)?;15921593 let item_owner = item.owner.clone();1594 Self::add_token_index(collection, current_index, &item.owner)?;15951596 <ItemListIndex>::insert(collection_id, current_index);1597 <NftItemList<T>>::insert(collection_id, current_index, item);15981599 1600 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1601 .checked_add(1)1602 .ok_or(Error::<T>::NumOverflow)?;1603 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);16041605 collection.log(ERC721Events::Transfer {1606 from: H160::default(),1607 to: *item_owner.as_eth(),1608 token_id: current_index.into(),1609 })?;1610 Self::deposit_event(RawEvent::ItemCreated(1611 collection_id,1612 current_index,1613 item_owner,1614 ));1615 Ok(())1616 }16171618 fn burn_refungible_item(1619 collection: &CollectionHandle<T>,1620 item_id: TokenId,1621 owner: &T::CrossAccountId,1622 ) -> DispatchResult {1623 let collection_id = collection.id;16241625 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1626 .ok_or(Error::<T>::TokenNotFound)?;1627 let rft_balance = token1628 .owner1629 .iter()1630 .find(|&i| i.owner == *owner)1631 .ok_or(Error::<T>::TokenNotFound)?;1632 Self::remove_token_index(collection, item_id, owner)?;16331634 1635 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1636 .checked_sub(rft_balance.fraction)1637 .ok_or(Error::<T>::NumOverflow)?;1638 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);16391640 1641 let index = token1642 .owner1643 .iter()1644 .position(|i| i.owner == *owner)1645 .expect("owned item is exists");1646 token.owner.remove(index);1647 let owner_count = token.owner.len();16481649 1650 if owner_count == 0 {1651 <ReFungibleItemList<T>>::remove(collection_id, item_id);1652 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1653 } else {1654 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1655 }16561657 Ok(())1658 }16591660 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1661 let collection_id = collection.id;16621663 let item =1664 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1665 Self::remove_token_index(collection, item_id, &item.owner)?;16661667 1668 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1669 .checked_sub(1)1670 .ok_or(Error::<T>::NumOverflow)?;1671 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1672 <NftItemList<T>>::remove(collection_id, item_id);1673 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);16741675 collection.log(ERC721Events::Transfer {1676 from: *item.owner.as_eth(),1677 to: H160::default(),1678 token_id: item_id.into(),1679 })?;1680 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1681 Ok(())1682 }16831684 fn burn_fungible_item(1685 owner: &T::CrossAccountId,1686 collection: &CollectionHandle<T>,1687 value: u128,1688 ) -> DispatchResult {1689 let collection_id = collection.id;16901691 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1692 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);16931694 1695 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1696 .checked_sub(value)1697 .ok_or(Error::<T>::NumOverflow)?;1698 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16991700 if balance.value - value > 0 {1701 balance.value -= value;1702 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1703 } else {1704 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1705 }17061707 collection.log(ERC20Events::Transfer {1708 from: *owner.as_eth(),1709 to: H160::default(),1710 value: value.into(),1711 })?;1712 Ok(())1713 }17141715 pub fn get_collection(1716 collection_id: CollectionId,1717 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1718 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1719 }17201721 fn check_owner_permissions(1722 target_collection: &CollectionHandle<T>,1723 subject: &T::AccountId,1724 ) -> DispatchResult {1725 ensure!(1726 *subject == target_collection.owner,1727 Error::<T>::NoPermission1728 );17291730 Ok(())1731 }17321733 fn is_owner_or_admin_permissions(1734 collection: &CollectionHandle<T>,1735 subject: &T::CrossAccountId,1736 ) -> Result<bool, DispatchError> {1737 collection.consume_sload()?;1738 Ok(*subject.as_sub() == collection.owner1739 || <AdminList<T>>::get(collection.id).contains(subject))1740 }17411742 fn check_owner_or_admin_permissions(1743 collection: &CollectionHandle<T>,1744 subject: &T::CrossAccountId,1745 ) -> DispatchResult {1746 ensure!(1747 Self::is_owner_or_admin_permissions(collection, subject)?,1748 Error::<T>::NoPermission1749 );17501751 Ok(())1752 }17531754 fn owned_amount(1755 subject: &T::CrossAccountId,1756 collection: &CollectionHandle<T>,1757 item_id: TokenId,1758 ) -> Result<Option<u128>, DispatchError> {1759 collection.consume_sload()?;1760 Ok(Self::owned_amount_unchecked(subject, collection, item_id))1761 }17621763 fn owned_amount_unchecked(1764 subject: &T::CrossAccountId,1765 target_collection: &CollectionHandle<T>,1766 item_id: TokenId,1767 ) -> Option<u128> {1768 let collection_id = target_collection.id;17691770 match target_collection.mode {1771 CollectionMode::NFT => {1772 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1773 }1774 CollectionMode::Fungible(_) => {1775 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1776 }1777 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1778 .owner1779 .iter()1780 .find(|i| i.owner == *subject)1781 .map(|i| i.fraction),1782 }1783 }17841785 fn is_item_owner(1786 subject: &T::CrossAccountId,1787 target_collection: &CollectionHandle<T>,1788 item_id: TokenId,1789 ) -> Result<bool, DispatchError> {1790 Ok(match target_collection.mode {1791 CollectionMode::Fungible(_) => true,1792 _ => Self::owned_amount(subject, target_collection, item_id)?.is_some(),1793 })1794 }17951796 fn check_white_list(1797 collection: &CollectionHandle<T>,1798 address: &T::CrossAccountId,1799 ) -> DispatchResult {1800 collection.consume_sload()?;1801 ensure!(1802 <WhiteList<T>>::contains_key(collection.id, address.as_sub()),1803 Error::<T>::AddresNotInWhiteList,1804 );1805 Ok(())1806 }18071808 1809 1810 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1811 let collection_id = target_collection.id;1812 let exists = match target_collection.mode {1813 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1814 CollectionMode::Fungible(_) => true,1815 CollectionMode::ReFungible => {1816 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1817 }1818 };18191820 ensure!(exists, Error::<T>::TokenNotFound);1821 Ok(())1822 }18231824 fn transfer_fungible(1825 collection: &CollectionHandle<T>,1826 value: u128,1827 owner: &T::CrossAccountId,1828 recipient: &T::CrossAccountId,1829 ) -> DispatchResult {1830 let collection_id = collection.id;18311832 collection.consume_sload()?;1833 collection.consume_sload()?;1834 let mut recipient_balance = <FungibleItemList<T>>::get(collection_id, recipient.as_sub());1835 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());18361837 recipient_balance.value = recipient_balance1838 .value1839 .checked_add(value)1840 .ok_or(Error::<T>::NumOverflow)?;1841 balance.value = balance1842 .value1843 .checked_sub(value)1844 .ok_or(Error::<T>::TokenValueTooLow)?;18451846 1847 collection.consume_sstore()?;1848 collection.consume_sstore()?;1849 if balance.value != 0 {1850 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value);1851 } else {1852 <Balance<T>>::remove(collection_id, owner.as_sub());1853 }1854 <Balance<T>>::insert(collection_id, recipient.as_sub(), recipient_balance.value);18551856 1857 collection.consume_sstore()?;1858 collection.consume_sstore()?;1859 if balance.value != 0 {1860 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1861 } else {1862 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1863 }1864 <FungibleItemList<T>>::insert(collection_id, recipient.as_sub(), recipient_balance);18651866 collection.log(ERC20Events::Transfer {1867 from: *owner.as_eth(),1868 to: *recipient.as_eth(),1869 value: value.into(),1870 })?;1871 Self::deposit_event(RawEvent::Transfer(1872 collection.id,1873 1,1874 owner.clone(),1875 recipient.clone(),1876 value,1877 ));18781879 Ok(())1880 }18811882 fn transfer_refungible(1883 collection: &CollectionHandle<T>,1884 item_id: TokenId,1885 value: u128,1886 owner: T::CrossAccountId,1887 new_owner: T::CrossAccountId,1888 ) -> DispatchResult {1889 let collection_id = collection.id;1890 collection.consume_sload()?;1891 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1892 .ok_or(Error::<T>::TokenNotFound)?;18931894 let item = full_item1895 .owner1896 .iter()1897 .find(|i| i.owner == owner)1898 .ok_or(Error::<T>::TokenNotFound)?;1899 let amount = item.fraction;19001901 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19021903 collection.consume_sload()?;1904 1905 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())1906 .checked_sub(value)1907 .ok_or(Error::<T>::NumOverflow)?;1908 collection.consume_sstore()?;1909 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);19101911 collection.consume_sload()?;1912 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())1913 .checked_add(value)1914 .ok_or(Error::<T>::NumOverflow)?;1915 collection.consume_sstore()?;1916 <Balance<T>>::insert(collection_id, new_owner.as_sub(), 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 let mut new_full_item = full_item.clone();1922 1923 if amount == value && !new_owner_has_account {1924 1925 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 collection.consume_sstore()?;1933 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19341935 1936 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;1937 } else {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, item_id, &new_owner)?;1961 }19621963 collection.consume_sstore()?;1964 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1965 }19661967 Self::deposit_event(RawEvent::Transfer(1968 collection.id,1969 item_id,1970 owner,1971 new_owner,1972 amount,1973 ));19741975 Ok(())1976 }19771978 fn transfer_nft(1979 collection: &CollectionHandle<T>,1980 item_id: TokenId,1981 sender: T::CrossAccountId,1982 new_owner: T::CrossAccountId,1983 ) -> DispatchResult {1984 let collection_id = collection.id;1985 collection.consume_sload()?;1986 let mut item =1987 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;19881989 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);19901991 collection.consume_sload()?;1992 1993 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())1994 .checked_sub(1)1995 .ok_or(Error::<T>::NumOverflow)?;1996 collection.consume_sstore()?;1997 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);19981999 collection.consume_sload()?;2000 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2001 .checked_add(1)2002 .ok_or(Error::<T>::NumOverflow)?;2003 collection.consume_sstore()?;2004 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20052006 2007 let old_owner = item.owner.clone();2008 item.owner = new_owner.clone();2009 collection.consume_sstore()?;2010 <NftItemList<T>>::insert(collection_id, item_id, item);20112012 2013 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;20142015 collection.log(ERC721Events::Transfer {2016 from: *sender.as_eth(),2017 to: *new_owner.as_eth(),2018 token_id: item_id.into(),2019 })?;2020 Self::deposit_event(RawEvent::Transfer(2021 collection.id,2022 item_id,2023 sender,2024 new_owner,2025 1,2026 ));20272028 Ok(())2029 }20302031 fn set_re_fungible_variable_data(2032 collection: &CollectionHandle<T>,2033 item_id: TokenId,2034 data: Vec<u8>,2035 ) -> DispatchResult {2036 let collection_id = collection.id;2037 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2038 .ok_or(Error::<T>::TokenNotFound)?;20392040 item.variable_data = data;20412042 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20432044 Ok(())2045 }20462047 fn set_nft_variable_data(2048 collection: &CollectionHandle<T>,2049 item_id: TokenId,2050 data: Vec<u8>,2051 ) -> DispatchResult {2052 let collection_id = collection.id;2053 let mut item =2054 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20552056 item.variable_data = data;20572058 <NftItemList<T>>::insert(collection_id, item_id, item);20592060 Ok(())2061 }20622063 #[allow(dead_code)]2064 fn init_collection(item: &Collection<T>) {2065 2066 assert!(2067 item.decimal_points <= MAX_DECIMAL_POINTS,2068 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2069 );2070 assert!(2071 item.name.len() <= 64,2072 "Collection name can not be longer than 63 char"2073 );2074 assert!(2075 item.name.len() <= 256,2076 "Collection description can not be longer than 255 char"2077 );2078 assert!(2079 item.token_prefix.len() <= 16,2080 "Token prefix can not be longer than 15 char"2081 );20822083 2084 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();20852086 CreatedCollectionCount::put(next_id);2087 }20882089 #[allow(dead_code)]2090 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2091 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();20922093 Self::add_token_index(2094 &CollectionHandle::get(collection_id).unwrap(),2095 current_index,2096 &item.owner,2097 )2098 .unwrap();20992100 <ItemListIndex>::insert(collection_id, current_index);21012102 2103 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2104 .checked_add(1)2105 .unwrap();2106 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2107 }21082109 #[allow(dead_code)]2110 fn init_fungible_token(2111 collection_id: CollectionId,2112 owner: &T::CrossAccountId,2113 item: &FungibleItemType,2114 ) {2115 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21162117 Self::add_token_index(2118 &CollectionHandle::get(collection_id).unwrap(),2119 current_index,2120 owner,2121 )2122 .unwrap();21232124 <ItemListIndex>::insert(collection_id, current_index);21252126 2127 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2128 .checked_add(item.value)2129 .unwrap();2130 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2131 }21322133 #[allow(dead_code)]2134 fn init_refungible_token(2135 collection_id: CollectionId,2136 item: &ReFungibleItemType<T::CrossAccountId>,2137 ) {2138 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21392140 let value = item.owner.first().unwrap().fraction;2141 let owner = item.owner.first().unwrap().owner.clone();21422143 Self::add_token_index(2144 &CollectionHandle::get(collection_id).unwrap(),2145 current_index,2146 &owner,2147 )2148 .unwrap();21492150 <ItemListIndex>::insert(collection_id, current_index);21512152 2153 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2154 .checked_add(value)2155 .unwrap();2156 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2157 }21582159 fn add_token_index(2160 collection: &CollectionHandle<T>,2161 item_index: TokenId,2162 owner: &T::CrossAccountId,2163 ) -> DispatchResult {2164 collection.consume_sload()?;2165 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2166 if list_exists {2167 collection.consume_sload()?;2168 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());21692170 2171 let account_items: u32 = list.len() as u32;2172 ensure!(2173 account_items < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2174 Error::<T>::AddressOwnershipLimitExceeded2175 );21762177 let item_contains = list.contains(&item_index.clone());21782179 if !item_contains {2180 list.push(item_index);2181 }21822183 collection.consume_sstore()?;2184 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2185 } else {2186 let itm = vec![item_index];2187 collection.consume_sstore()?;2188 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), itm);2189 }21902191 Ok(())2192 }21932194 fn remove_token_index(2195 collection: &CollectionHandle<T>,2196 item_index: TokenId,2197 owner: &T::CrossAccountId,2198 ) -> DispatchResult {2199 collection.consume_sload()?;2200 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2201 if list_exists {2202 collection.consume_sload()?;2203 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2204 let item_contains = list.contains(&item_index.clone());22052206 if item_contains {2207 list.retain(|&item| item != item_index);2208 collection.consume_sstore()?;2209 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2210 }2211 }22122213 Ok(())2214 }22152216 fn move_token_index(2217 collection: &CollectionHandle<T>,2218 item_index: TokenId,2219 old_owner: &T::CrossAccountId,2220 new_owner: &T::CrossAccountId,2221 ) -> DispatchResult {2222 Self::remove_token_index(collection, item_index, old_owner)?;2223 Self::add_token_index(collection, item_index, new_owner)?;22242225 Ok(())2226 }2227}22282229sp_api::decl_runtime_apis! {2230 pub trait NftApi {2231 2232 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2233 }2234}