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::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, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45 FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod 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;6667pub trait WeightInfo {68 fn create_collection() -> Weight;69 fn destroy_collection() -> Weight;70 fn add_to_white_list() -> Weight;71 fn remove_from_white_list() -> Weight;72 fn set_public_access_mode() -> Weight;73 fn set_mint_permission() -> Weight;74 fn change_collection_owner() -> Weight;75 fn add_collection_admin() -> Weight;76 fn remove_collection_admin() -> Weight;77 fn set_collection_sponsor() -> Weight;78 fn confirm_sponsorship() -> Weight;79 fn remove_collection_sponsor() -> Weight;80 fn create_item(s: usize) -> Weight;81 fn burn_item() -> Weight;82 fn transfer() -> Weight;83 fn approve() -> Weight;84 fn transfer_from() -> Weight;85 fn set_offchain_schema() -> Weight;86 fn set_const_on_chain_schema() -> Weight;87 fn set_variable_on_chain_schema() -> Weight;88 fn set_variable_meta_data() -> Weight;89 fn enable_contract_sponsoring() -> Weight;90 fn set_schema_version() -> Weight;91 fn set_contract_sponsoring_rate_limit() -> Weight;92 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;93 fn toggle_contract_white_list() -> Weight;94 fn add_to_contract_white_list() -> Weight;95 fn remove_from_contract_white_list() -> Weight;96 fn set_collection_limits() -> Weight;97}9899decl_error! {100 101 pub enum Error for Module<T: Config> {102 103 TotalCollectionsLimitExceeded,104 105 CollectionDecimalPointLimitExceeded,106 107 CollectionNameLimitExceeded,108 109 CollectionDescriptionLimitExceeded,110 111 CollectionTokenPrefixLimitExceeded,112 113 CollectionNotFound,114 115 TokenNotFound,116 117 AdminNotFound,118 119 NumOverflow,120 121 AlreadyAdmin,122 123 NoPermission,124 125 ConfirmUnsetSponsorFail,126 127 PublicMintingNotAllowed,128 129 MustBeTokenOwner,130 131 TokenValueTooLow,132 133 NftSizeLimitExceeded,134 135 ApproveNotFound,136 137 TokenValueNotEnough,138 139 ApproveRequired,140 141 AddresNotInWhiteList,142 143 CollectionAdminsLimitExceeded,144 145 AddressOwnershipLimitExceeded,146 147 EmptyArgument,148 149 TokenConstDataLimitExceeded,150 151 TokenVariableDataLimitExceeded,152 153 NotNftDataUsedToMintNftCollectionToken,154 155 NotFungibleDataUsedToMintFungibleCollectionToken,156 157 NotReFungibleDataUsedToMintReFungibleCollectionToken,158 159 UnexpectedCollectionType,160 161 CantStoreMetadataInFungibleTokens,162 163 CollectionTokenLimitExceeded,164 165 AccountTokenLimitExceeded,166 167 CollectionLimitBoundsExceeded,168 169 OwnerPermissionsCantBeReverted,170 171 SchemaDataLimitExceeded,172 173 WrongRefungiblePieces,174 175 BadCreateRefungibleCall,176 177 OutOfGas,178 179 TransferNotAllowed,180 181 AddressIsZero,182 }183}184185#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]186pub struct CollectionHandle<T: Config> {187 pub id: CollectionId,188 collection: Collection<T>,189 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,190}191impl<T: Config> CollectionHandle<T> {192 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {193 <CollectionById<T>>::get(id).map(|collection| Self {194 id,195 collection,196 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(197 eth::collection_id_to_address(id),198 gas_limit,199 ),200 })201 }202 pub fn get(id: CollectionId) -> Option<Self> {203 Self::get_with_gas_limit(id, u64::MAX)204 }205 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {206 self.recorder.log_sub(log)207 }208 fn consume_gas(&self, gas: u64) -> DispatchResult {209 self.recorder.consume_gas_sub(gas)210 }211 pub fn submit_logs(self) -> DispatchResult {212 self.recorder.submit_logs()213 }214 pub fn save(self) -> DispatchResult {215 self.recorder.submit_logs()?;216 <CollectionById<T>>::insert(self.id, self.collection);217 Ok(())218 }219}220impl<T: Config> Deref for CollectionHandle<T> {221 type Target = Collection<T>;222223 fn deref(&self) -> &Self::Target {224 &self.collection225 }226}227228impl<T: Config> DerefMut for CollectionHandle<T> {229 fn deref_mut(&mut self) -> &mut Self::Target {230 &mut self.collection231 }232}233234pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {235 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;236237 238 type WeightInfo: WeightInfo;239240 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;241 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;242243 type CrossAccountId: CrossAccountId<Self::AccountId>;244 type Currency: Currency<Self::AccountId>;245 type CollectionCreationPrice: Get<246 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,247 >;248 type TreasuryAccountId: Get<Self::AccountId>;249}250251252253254255256257258259260261262263264265266267268269270271272273decl_storage! {274 trait Store for Module<T: Config> as Nft {275276 277 278 CreatedCollectionCount: u32;279 280 ChainVersion: u64;281 282 283 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;284 285286 287 288 289 DestroyedCollectionCount: u32;290 291 292 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;293 294295 296 297 298 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;299 300 301 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;302 303 304 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;305 306307 308 309 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;310311 312 313 314 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;315316 317 318 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;319 320 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;321 322 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;323 324325 326 327 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;328 329330 331 332 333 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;334 335 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;336 337 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;338 339 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;340 341342 343 344 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;345 }346 add_extra_genesis {347 build(|config: &GenesisConfig<T>| {348 349 for (_num, _c) in &config.collection_id {350 <Module<T>>::init_collection(_c);351 }352353 for (_num, _c, _i) in &config.nft_item_id {354 <Module<T>>::init_nft_token(*_c, _i);355 }356357 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {358 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);359 }360361 for (_num, _c, _i) in &config.refungible_item_id {362 <Module<T>>::init_refungible_token(*_c, _i);363 }364 })365 }366}367368decl_event!(369 pub enum Event<T>370 where371 AccountId = <T as frame_system::Config>::AccountId,372 CrossAccountId = <T as Config>::CrossAccountId,373 {374 375 376 377 378 379 380 381 382 383 CollectionCreated(CollectionId, u8, AccountId),384385 386 387 388 389 390 391 392 393 394 ItemCreated(CollectionId, TokenId, CrossAccountId),395396 397 398 399 400 401 402 403 ItemDestroyed(CollectionId, TokenId),404405 406 407 408 409 410 411 412 413 414 415 416 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),417418 419 420 421 422 423 424 425 426 427 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),428 }429);430431decl_module! {432 pub struct Module<T: Config> for enum Call433 where434 origin: T::Origin435 {436 fn deposit_event() = default;437 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;438 type Error = Error<T>;439440 fn on_initialize(_now: T::BlockNumber) -> Weight {441 0442 }443444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 #[weight = <T as Config>::WeightInfo::create_collection()]461 #[transactional]462 pub fn create_collection(origin,463 collection_name: Vec<u16>,464 collection_description: Vec<u16>,465 token_prefix: Vec<u8>,466 mode: CollectionMode) -> DispatchResult {467468 469 let who = ensure_signed(origin)?;470471 472 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();473 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(474 &T::TreasuryAccountId::get(),475 T::CollectionCreationPrice::get(),476 ));477 <T as Config>::Currency::settle(478 &who,479 imbalance,480 WithdrawReasons::TRANSFER,481 ExistenceRequirement::KeepAlive,482 ).map_err(|_| Error::<T>::NoPermission)?;483484 let decimal_points = match mode {485 CollectionMode::Fungible(points) => points,486 _ => 0487 };488489 let created_count = CreatedCollectionCount::get();490 let destroyed_count = DestroyedCollectionCount::get();491492 493 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);494495 496 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);497 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);498 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);499 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);500501 502 let next_id = created_count503 .checked_add(1)504 .ok_or(Error::<T>::NumOverflow)?;505506 CreatedCollectionCount::put(next_id);507508 let limits = CollectionLimits {509 sponsored_data_size: CUSTOM_DATA_LIMIT,510 ..Default::default()511 };512513 514 let new_collection = Collection {515 owner: who.clone(),516 name: collection_name,517 mode: mode.clone(),518 mint_mode: false,519 access: AccessMode::Normal,520 description: collection_description,521 decimal_points,522 token_prefix,523 offchain_schema: Vec::new(),524 schema_version: SchemaVersion::ImageURL,525 sponsorship: SponsorshipState::Disabled,526 variable_on_chain_schema: Vec::new(),527 const_on_chain_schema: Vec::new(),528 limits,529 transfers_enabled: true,530 };531532 533 <CollectionById<T>>::insert(next_id, new_collection);534535 536 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));537538 Ok(())539 }540541 542 543 544 545 546 547 548 549 550 #[weight = <T as Config>::WeightInfo::destroy_collection()]551 #[transactional]552 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {553554 let sender = ensure_signed(origin)?;555 let collection = Self::get_collection(collection_id)?;556 Self::check_owner_permissions(&collection, &sender)?;557 if !collection.limits.owner_can_destroy {558 fail!(Error::<T>::NoPermission);559 }560561 <AddressTokens<T>>::remove_prefix(collection_id, None);562 <Allowances<T>>::remove_prefix(collection_id, None);563 <Balance<T>>::remove_prefix(collection_id, None);564 <ItemListIndex>::remove(collection_id);565 <AdminList<T>>::remove(collection_id);566 <CollectionById<T>>::remove(collection_id);567 <WhiteList<T>>::remove_prefix(collection_id, None);568569 <NftItemList<T>>::remove_prefix(collection_id, None);570 <FungibleItemList<T>>::remove_prefix(collection_id, None);571 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);572573 <NftTransferBasket<T>>::remove_prefix(collection_id, None);574 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);575 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);576577 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);578579 DestroyedCollectionCount::put(DestroyedCollectionCount::get()580 .checked_add(1)581 .ok_or(Error::<T>::NumOverflow)?);582583 Ok(())584 }585586 587 588 589 590 591 592 593 594 595 596 597 598 #[weight = <T as Config>::WeightInfo::add_to_white_list()]599 #[transactional]600 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{601602 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);603 let collection = Self::get_collection(collection_id)?;604605 Self::toggle_white_list_internal(606 &sender,607 &collection,608 &address,609 true,610 )?;611612 Ok(())613 }614615 616 617 618 619 620 621 622 623 624 625 626 627 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]628 #[transactional]629 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{630631 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);632 let collection = Self::get_collection(collection_id)?;633634 Self::toggle_white_list_internal(635 &sender,636 &collection,637 &address,638 false,639 )?;640641 Ok(())642 }643644 645 646 647 648 649 650 651 652 653 654 655 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]656 #[transactional]657 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult658 {659 let sender = ensure_signed(origin)?;660661 let mut target_collection = Self::get_collection(collection_id)?;662 Self::check_owner_permissions(&target_collection, &sender)?;663 target_collection.access = mode;664 target_collection.save()665 }666667 668 669 670 671 672 673 674 675 676 677 678 679 680 #[weight = <T as Config>::WeightInfo::set_mint_permission()]681 #[transactional]682 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult683 {684 let sender = ensure_signed(origin)?;685686 let mut target_collection = Self::get_collection(collection_id)?;687 Self::check_owner_permissions(&target_collection, &sender)?;688 target_collection.mint_mode = mint_permission;689 target_collection.save()690 }691692 693 694 695 696 697 698 699 700 701 702 703 #[weight = <T as Config>::WeightInfo::change_collection_owner()]704 #[transactional]705 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {706707 let sender = ensure_signed(origin)?;708 let mut target_collection = Self::get_collection(collection_id)?;709 Self::check_owner_permissions(&target_collection, &sender)?;710 target_collection.owner = new_owner;711 target_collection.save()712 }713714 715 716 717 718 719 720 721 722 723 724 725 726 727 #[weight = <T as Config>::WeightInfo::add_collection_admin()]728 #[transactional]729 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {730 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);731 let collection = Self::get_collection(collection_id)?;732 Self::check_owner_or_admin_permissions(&collection, &sender)?;733 let mut admin_arr = <AdminList<T>>::get(collection_id);734735 match admin_arr.binary_search(&new_admin_id) {736 Ok(_) => {},737 Err(idx) => {738 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);739 admin_arr.insert(idx, new_admin_id);740 <AdminList<T>>::insert(collection_id, admin_arr);741 }742 }743 Ok(())744 }745746 747 748 749 750 751 752 753 754 755 756 757 758 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]759 #[transactional]760 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {761 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);762 let collection = Self::get_collection(collection_id)?;763 Self::check_owner_or_admin_permissions(&collection, &sender)?;764 let mut admin_arr = <AdminList<T>>::get(collection_id);765766 if let Ok(idx) = admin_arr.binary_search(&account_id) {767 admin_arr.remove(idx);768 <AdminList<T>>::insert(collection_id, admin_arr);769 }770 Ok(())771 }772773 774 775 776 777 778 779 780 781 782 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]783 #[transactional]784 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {785 let sender = ensure_signed(origin)?;786 let mut target_collection = Self::get_collection(collection_id)?;787 Self::check_owner_permissions(&target_collection, &sender)?;788789 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);790 target_collection.save()791 }792793 794 795 796 797 798 799 800 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]801 #[transactional]802 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {803 let sender = ensure_signed(origin)?;804805 let mut target_collection = Self::get_collection(collection_id)?;806 ensure!(807 target_collection.sponsorship.pending_sponsor() == Some(&sender),808 Error::<T>::ConfirmUnsetSponsorFail809 );810811 target_collection.sponsorship = SponsorshipState::Confirmed(sender);812 target_collection.save()813 }814815 816 817 818 819 820 821 822 823 824 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]825 #[transactional]826 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {827 let sender = ensure_signed(origin)?;828829 let mut target_collection = Self::get_collection(collection_id)?;830 Self::check_owner_permissions(&target_collection, &sender)?;831832 target_collection.sponsorship = SponsorshipState::Disabled;833 target_collection.save()834 }835836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859860 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]861 #[transactional]862 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {863 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);864 let collection = Self::get_collection(collection_id)?;865866 Self::create_item_internal(&sender, &collection, &owner, data)?;867868 collection.submit_logs()869 }870871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()890 .map(|data| { data.data_size() })891 .sum())]892 #[transactional]893 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {894895 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);896 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);897 let collection = Self::get_collection(collection_id)?;898899 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;900901 collection.submit_logs()902 }903904 905906 907 908 909 910 911 912 913 914 915 916 917 #[weight = <T as Config>::WeightInfo::burn_item()]918 #[transactional]919 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {920921 let sender = ensure_signed(origin)?;922 let mut target_collection = Self::get_collection(collection_id)?;923924 Self::check_owner_permissions(&target_collection, &sender)?;925926 target_collection.transfers_enabled = value;927 target_collection.save()928 }929930 931 932 933 934 935 936 937 938 939 940 941 942 943 #[weight = <T as Config>::WeightInfo::burn_item()]944 #[transactional]945 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {946947 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);948 let target_collection = Self::get_collection(collection_id)?;949950 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;951952 target_collection.submit_logs()953 }954955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 #[weight = <T as Config>::WeightInfo::transfer()]979 #[transactional]980 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {981 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);982 let collection = Self::get_collection(collection_id)?;983984 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;985986 collection.submit_logs()987 }988989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 #[weight = <T as Config>::WeightInfo::approve()]1005 #[transactional]1006 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1007 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1008 let collection = Self::get_collection(collection_id)?;10091010 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10111012 collection.submit_logs()1013 }10141015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 #[weight = <T as Config>::WeightInfo::transfer_from()]1035 #[transactional]1036 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1037 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038 let collection = Self::get_collection(collection_id)?;10391040 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10411042 collection.submit_logs()1043 }1044 1045 1046 1047 1048 10491050 10511052 10531054 1055 10561057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1070 #[transactional]1071 pub fn set_variable_meta_data (1072 origin,1073 collection_id: CollectionId,1074 item_id: TokenId,1075 data: Vec<u8>1076 ) -> DispatchResult {1077 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10781079 let collection = Self::get_collection(collection_id)?;10801081 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10821083 Ok(())1084 }10851086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 #[weight = <T as Config>::WeightInfo::set_schema_version()]1101 #[transactional]1102 pub fn set_schema_version(1103 origin,1104 collection_id: CollectionId,1105 version: SchemaVersion1106 ) -> DispatchResult {1107 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1108 let mut target_collection = Self::get_collection(collection_id)?;1109 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1110 target_collection.schema_version = version;1111 target_collection.save()1112 }11131114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1127 #[transactional]1128 pub fn set_offchain_schema(1129 origin,1130 collection_id: CollectionId,1131 schema: Vec<u8>1132 ) -> DispatchResult {1133 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1134 let mut target_collection = Self::get_collection(collection_id)?;1135 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11361137 1138 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11391140 target_collection.offchain_schema = schema;1141 target_collection.save()1142 }11431144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1157 #[transactional]1158 pub fn set_const_on_chain_schema (1159 origin,1160 collection_id: CollectionId,1161 schema: Vec<u8>1162 ) -> DispatchResult {1163 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1164 let mut target_collection = Self::get_collection(collection_id)?;1165 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11661167 1168 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11691170 target_collection.const_on_chain_schema = schema;1171 target_collection.save()1172 }11731174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1187 #[transactional]1188 pub fn set_variable_on_chain_schema (1189 origin,1190 collection_id: CollectionId,1191 schema: Vec<u8>1192 ) -> DispatchResult {1193 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1194 let mut target_collection = Self::get_collection(collection_id)?;1195 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11961197 1198 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11991200 target_collection.variable_on_chain_schema = schema;1201 target_collection.save()1202 }12031204 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1205 #[transactional]1206 pub fn set_collection_limits(1207 origin,1208 collection_id: u32,1209 new_limits: CollectionLimits<T::BlockNumber>,1210 ) -> DispatchResult {1211 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1212 let mut target_collection = Self::get_collection(collection_id)?;1213 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1214 let old_limits = &target_collection.limits;12151216 1217 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1218 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1219 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1220 Error::<T>::CollectionLimitBoundsExceeded);12211222 1223 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1224 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12251226 ensure!(1227 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1228 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1229 Error::<T>::OwnerPermissionsCantBeReverted,1230 );12311232 target_collection.limits = new_limits;12331234 target_collection.save()1235 }1236 }1237}12381239impl<T: Config> Module<T> {1240 pub fn create_item_internal(1241 sender: &T::CrossAccountId,1242 collection: &CollectionHandle<T>,1243 owner: &T::CrossAccountId,1244 data: CreateItemData,1245 ) -> DispatchResult {1246 ensure!(1247 owner != &T::CrossAccountId::from_eth(H160([0; 20])),1248 Error::<T>::AddressIsZero1249 );12501251 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1252 Self::validate_create_item_args(collection, &data)?;1253 Self::create_item_no_validation(collection, owner, data)?;1254 pub fn transfer_internal(1255 sender: &T::CrossAccountId,1256 recipient: &T::CrossAccountId,1257 target_collection: &CollectionHandle<T>,1258 item_id: TokenId,1259 value: u128,1260 ) -> DispatchResult {1261 ensure!(1262 recipient != &T::CrossAccountId::from_eth(H160([0; 20])),1263 Error::<T>::AddressIsZero1264 );12651266 target_collection.consume_gas(2000000)?;1267 1268 Self::is_correct_transfer(target_collection, recipient)?;12691270 1271 ensure!(1272 Self::is_item_owner(sender, target_collection, item_id)1273 || Self::is_owner_or_admin_permissions(target_collection, sender),1274 Error::<T>::NoPermission1275 );12761277 if target_collection.access == AccessMode::WhiteList {1278 Self::check_white_list(target_collection, sender)?;1279 Self::check_white_list(target_collection, recipient)?;1280 }12811282 match target_collection.mode {1283 CollectionMode::NFT => Self::transfer_nft(1284 target_collection,1285 item_id,1286 sender.clone(),1287 recipient.clone(),1288 )?,1289 CollectionMode::Fungible(_) => {1290 Self::transfer_fungible(target_collection, value, sender, recipient)?1291 }1292 CollectionMode::ReFungible => Self::transfer_refungible(1293 target_collection,1294 item_id,1295 value,1296 sender.clone(),1297 recipient.clone(),1298 )?,1299 _ => (),1300 };13011302 Self::deposit_event(RawEvent::Transfer(1303 target_collection.id,1304 item_id,1305 sender.clone(),1306 recipient.clone(),1307 value,1308 ));13091310 Ok(())1311 }13121313 pub fn approve_internal(1314 sender: &T::CrossAccountId,1315 spender: &T::CrossAccountId,1316 collection: &CollectionHandle<T>,1317 item_id: TokenId,1318 amount: u128,1319 ) -> DispatchResult {1320 collection.consume_gas(2000000)?;1321 Self::token_exists(collection, item_id)?;13221323 1324 let bypasses_limits = collection.limits.owner_can_transfer1325 && Self::is_owner_or_admin_permissions(collection, sender);13261327 let allowance_limit = if bypasses_limits {1328 None1329 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1330 Some(amount)1331 } else {1332 fail!(Error::<T>::NoPermission);1333 };13341335 if collection.access == AccessMode::WhiteList {1336 Self::check_white_list(collection, sender)?;1337 Self::check_white_list(collection, spender)?;1338 }13391340 let allowance: u128 = amount1341 .checked_add(<Allowances<T>>::get(1342 collection.id,1343 (item_id, sender.as_sub(), spender.as_sub()),1344 ))1345 .ok_or(Error::<T>::NumOverflow)?;1346 if let Some(limit) = allowance_limit {1347 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1348 }1349 <Allowances<T>>::insert(1350 collection.id,1351 (item_id, sender.as_sub(), spender.as_sub()),1352 allowance,1353 );13541355 if matches!(collection.mode, CollectionMode::NFT) {1356 1357 collection.log(ERC721Events::Approval {1358 owner: *sender.as_eth(),1359 approved: *spender.as_eth(),1360 token_id: item_id.into(),1361 })?;1362 }13631364 if matches!(collection.mode, CollectionMode::Fungible(_)) {1365 1366 collection.log(ERC20Events::Approval {1367 owner: *sender.as_eth(),1368 spender: *spender.as_eth(),1369 value: allowance.into(),1370 })?;1371 }13721373 Self::deposit_event(RawEvent::Approved(1374 collection.id,1375 item_id,1376 sender.clone(),1377 spender.clone(),1378 allowance,1379 ));1380 Ok(())1381 }13821383 pub fn transfer_from_internal(1384 sender: &T::CrossAccountId,1385 from: &T::CrossAccountId,1386 recipient: &T::CrossAccountId,1387 collection: &CollectionHandle<T>,1388 item_id: TokenId,1389 amount: u128,1390 ) -> DispatchResult {1391 collection.consume_gas(2000000)?;1392 1393 let approval: u128 =1394 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13951396 1397 Self::is_correct_transfer(collection, recipient)?;13981399 1400 ensure!(1401 approval >= amount1402 || (collection.limits.owner_can_transfer1403 && Self::is_owner_or_admin_permissions(collection, sender)),1404 Error::<T>::NoPermission1405 );14061407 if collection.access == AccessMode::WhiteList {1408 Self::check_white_list(collection, sender)?;1409 Self::check_white_list(collection, recipient)?;1410 }14111412 1413 let allowance = approval.saturating_sub(amount);1414 if allowance > 0 {1415 <Allowances<T>>::insert(1416 collection.id,1417 (item_id, from.as_sub(), sender.as_sub()),1418 allowance,1419 );1420 } else {1421 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1422 }14231424 match collection.mode {1425 CollectionMode::NFT => {1426 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1427 }1428 CollectionMode::Fungible(_) => {1429 Self::transfer_fungible(collection, amount, from, recipient)?1430 }1431 CollectionMode::ReFungible => Self::transfer_refungible(1432 collection,1433 item_id,1434 amount,1435 from.clone(),1436 recipient.clone(),1437 )?,1438 _ => (),1439 };14401441 if matches!(collection.mode, CollectionMode::Fungible(_)) {1442 collection.log(ERC20Events::Approval {1443 owner: *from.as_eth(),1444 spender: *sender.as_eth(),1445 value: allowance.into(),1446 })?;1447 }14481449 Ok(())1450 }14511452 pub fn set_variable_meta_data_internal(1453 sender: &T::CrossAccountId,1454 collection: &CollectionHandle<T>,1455 item_id: TokenId,1456 data: Vec<u8>,1457 ) -> DispatchResult {1458 Self::token_exists(collection, item_id)?;14591460 ensure!(1461 CUSTOM_DATA_LIMIT >= data.len() as u32,1462 Error::<T>::TokenVariableDataLimitExceeded1463 );14641465 1466 ensure!(1467 Self::is_item_owner(sender, collection, item_id)1468 || Self::is_owner_or_admin_permissions(collection, sender),1469 Error::<T>::NoPermission1470 );14711472 match collection.mode {1473 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1474 CollectionMode::ReFungible => {1475 Self::set_re_fungible_variable_data(collection, item_id, data)?1476 }1477 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1478 _ => fail!(Error::<T>::UnexpectedCollectionType),1479 };14801481 Ok(())1482 }14831484 pub fn create_multiple_items_internal(1485 sender: &T::CrossAccountId,1486 collection: &CollectionHandle<T>,1487 owner: &T::CrossAccountId,1488 items_data: Vec<CreateItemData>,1489 ) -> DispatchResult {1490 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14911492 for data in &items_data {1493 Self::validate_create_item_args(collection, data)?;1494 }1495 for data in &items_data {1496 Self::create_item_no_validation(collection, owner, data.clone())?;1497 }14981499 Ok(())1500 }15011502 pub fn burn_item_internal(1503 sender: &T::CrossAccountId,1504 collection: &CollectionHandle<T>,1505 item_id: TokenId,1506 value: u128,1507 ) -> DispatchResult {1508 ensure!(1509 Self::is_item_owner(sender, collection, item_id)1510 || (collection.limits.owner_can_transfer1511 && Self::is_owner_or_admin_permissions(collection, sender)),1512 Error::<T>::NoPermission1513 );15141515 if collection.access == AccessMode::WhiteList {1516 Self::check_white_list(collection, sender)?;1517 }15181519 match collection.mode {1520 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1521 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1522 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1523 _ => (),1524 };15251526 Ok(())1527 }15281529 pub fn toggle_white_list_internal(1530 sender: &T::CrossAccountId,1531 collection: &CollectionHandle<T>,1532 address: &T::CrossAccountId,1533 whitelisted: bool,1534 ) -> DispatchResult {1535 Self::check_owner_or_admin_permissions(collection, sender)?;15361537 if whitelisted {1538 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1539 } else {1540 <WhiteList<T>>::remove(collection.id, address.as_sub());1541 }15421543 Ok(())1544 }15451546 fn is_correct_transfer(1547 collection: &CollectionHandle<T>,1548 recipient: &T::CrossAccountId,1549 ) -> DispatchResult {1550 let collection_id = collection.id;15511552 1553 let account_items: u32 =1554 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1555 ensure!(1556 collection.limits.account_token_ownership_limit > account_items,1557 Error::<T>::AccountTokenLimitExceeded1558 );15591560 1561 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15621563 Ok(())1564 }15651566 fn can_create_items_in_collection(1567 collection: &CollectionHandle<T>,1568 sender: &T::CrossAccountId,1569 owner: &T::CrossAccountId,1570 amount: u32,1571 ) -> DispatchResult {1572 let collection_id = collection.id;15731574 1575 let total_items: u32 = ItemListIndex::get(collection_id)1576 .checked_add(amount)1577 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1578 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1579 as u32)1580 .checked_add(amount)1581 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1582 ensure!(1583 collection.limits.token_limit >= total_items,1584 Error::<T>::CollectionTokenLimitExceeded1585 );1586 ensure!(1587 collection.limits.account_token_ownership_limit >= account_items,1588 Error::<T>::AccountTokenLimitExceeded1589 );15901591 if !Self::is_owner_or_admin_permissions(collection, sender) {1592 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1593 Self::check_white_list(collection, owner)?;1594 Self::check_white_list(collection, sender)?;1595 }15961597 Ok(())1598 }15991600 fn validate_create_item_args(1601 target_collection: &CollectionHandle<T>,1602 data: &CreateItemData,1603 ) -> DispatchResult {1604 match target_collection.mode {1605 CollectionMode::NFT => {1606 if !matches!(data, CreateItemData::NFT(_)) {1607 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1608 }1609 }1610 CollectionMode::Fungible(_) => {1611 if !matches!(data, CreateItemData::Fungible(_)) {1612 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1613 }1614 }1615 CollectionMode::ReFungible => {1616 if let CreateItemData::ReFungible(data) = data {1617 1618 ensure!(1619 data.pieces <= MAX_REFUNGIBLE_PIECES,1620 Error::<T>::WrongRefungiblePieces1621 );1622 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1623 } else {1624 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1625 }1626 }1627 _ => {1628 fail!(Error::<T>::UnexpectedCollectionType);1629 }1630 };16311632 Ok(())1633 }16341635 fn create_item_no_validation(1636 collection: &CollectionHandle<T>,1637 owner: &T::CrossAccountId,1638 data: CreateItemData,1639 ) -> DispatchResult {1640 match data {1641 CreateItemData::NFT(data) => {1642 let item = NftItemType {1643 owner: owner.clone(),1644 const_data: data.const_data.into_inner(),1645 variable_data: data.variable_data.into_inner(),1646 };16471648 Self::add_nft_item(collection, item)?;1649 }1650 CreateItemData::Fungible(data) => {1651 Self::add_fungible_item(collection, owner, data.value)?;1652 }1653 CreateItemData::ReFungible(data) => {1654 let owner_list = vec![Ownership {1655 owner: owner.clone(),1656 fraction: data.pieces,1657 }];16581659 let item = ReFungibleItemType {1660 owner: owner_list,1661 const_data: data.const_data.into_inner(),1662 variable_data: data.variable_data.into_inner(),1663 };16641665 Self::add_refungible_item(collection, item)?;1666 }1667 };16681669 Ok(())1670 }16711672 fn add_fungible_item(1673 collection: &CollectionHandle<T>,1674 owner: &T::CrossAccountId,1675 value: u128,1676 ) -> DispatchResult {1677 let collection_id = collection.id;16781679 1680 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16811682 1683 let item = FungibleItemType {1684 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1685 };1686 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16871688 1689 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1690 .checked_add(value)1691 .ok_or(Error::<T>::NumOverflow)?;1692 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16931694 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1695 Ok(())1696 }16971698 fn add_refungible_item(1699 collection: &CollectionHandle<T>,1700 item: ReFungibleItemType<T::CrossAccountId>,1701 ) -> DispatchResult {1702 let collection_id = collection.id;17031704 let current_index = <ItemListIndex>::get(collection_id)1705 .checked_add(1)1706 .ok_or(Error::<T>::NumOverflow)?;1707 let itemcopy = item.clone();17081709 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1710 let item_owner = item.owner.first().expect("only one owner is defined");17111712 let value = item_owner.fraction;1713 let owner = item_owner.owner.clone();17141715 Self::add_token_index(collection_id, current_index, &owner)?;17161717 <ItemListIndex>::insert(collection_id, current_index);1718 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17191720 1721 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1722 .checked_add(value)1723 .ok_or(Error::<T>::NumOverflow)?;1724 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17251726 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1727 Ok(())1728 }17291730 fn add_nft_item(1731 collection: &CollectionHandle<T>,1732 item: NftItemType<T::CrossAccountId>,1733 ) -> DispatchResult {1734 let collection_id = collection.id;17351736 let current_index = <ItemListIndex>::get(collection_id)1737 .checked_add(1)1738 .ok_or(Error::<T>::NumOverflow)?;17391740 let item_owner = item.owner.clone();1741 Self::add_token_index(collection_id, current_index, &item.owner)?;17421743 <ItemListIndex>::insert(collection_id, current_index);1744 <NftItemList<T>>::insert(collection_id, current_index, item);17451746 1747 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1748 .checked_add(1)1749 .ok_or(Error::<T>::NumOverflow)?;1750 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17511752 collection.log(ERC721Events::Transfer {1753 from: H160::default(),1754 to: *item_owner.as_eth(),1755 token_id: current_index.into(),1756 })?;1757 Self::deposit_event(RawEvent::ItemCreated(1758 collection_id,1759 current_index,1760 item_owner,1761 ));1762 Ok(())1763 }17641765 fn burn_refungible_item(1766 collection: &CollectionHandle<T>,1767 item_id: TokenId,1768 owner: &T::CrossAccountId,1769 ) -> DispatchResult {1770 let collection_id = collection.id;17711772 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1773 .ok_or(Error::<T>::TokenNotFound)?;1774 let rft_balance = token1775 .owner1776 .iter()1777 .find(|&i| i.owner == *owner)1778 .ok_or(Error::<T>::TokenNotFound)?;1779 Self::remove_token_index(collection_id, item_id, owner)?;17801781 1782 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1783 .checked_sub(rft_balance.fraction)1784 .ok_or(Error::<T>::NumOverflow)?;1785 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17861787 1788 let index = token1789 .owner1790 .iter()1791 .position(|i| i.owner == *owner)1792 .expect("owned item is exists");1793 token.owner.remove(index);1794 let owner_count = token.owner.len();17951796 1797 if owner_count == 0 {1798 <ReFungibleItemList<T>>::remove(collection_id, item_id);1799 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1800 } else {1801 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1802 }18031804 Ok(())1805 }18061807 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1808 let collection_id = collection.id;18091810 let item =1811 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1812 Self::remove_token_index(collection_id, item_id, &item.owner)?;18131814 1815 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1816 .checked_sub(1)1817 .ok_or(Error::<T>::NumOverflow)?;1818 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1819 <NftItemList<T>>::remove(collection_id, item_id);1820 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18211822 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1823 Ok(())1824 }18251826 fn burn_fungible_item(1827 owner: &T::CrossAccountId,1828 collection: &CollectionHandle<T>,1829 value: u128,1830 ) -> DispatchResult {1831 let collection_id = collection.id;18321833 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1834 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18351836 1837 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1838 .checked_sub(value)1839 .ok_or(Error::<T>::NumOverflow)?;1840 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18411842 if balance.value - value > 0 {1843 balance.value -= value;1844 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1845 } else {1846 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1847 }18481849 collection.log(ERC20Events::Transfer {1850 from: *owner.as_eth(),1851 to: H160::default(),1852 value: value.into(),1853 })?;1854 Ok(())1855 }18561857 pub fn get_collection(1858 collection_id: CollectionId,1859 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1860 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1861 }18621863 fn check_owner_permissions(1864 target_collection: &CollectionHandle<T>,1865 subject: &T::AccountId,1866 ) -> DispatchResult {1867 ensure!(1868 *subject == target_collection.owner,1869 Error::<T>::NoPermission1870 );18711872 Ok(())1873 }18741875 fn is_owner_or_admin_permissions(1876 collection: &CollectionHandle<T>,1877 subject: &T::CrossAccountId,1878 ) -> bool {1879 *subject.as_sub() == collection.owner1880 || <AdminList<T>>::get(collection.id).contains(subject)1881 }18821883 fn check_owner_or_admin_permissions(1884 collection: &CollectionHandle<T>,1885 subject: &T::CrossAccountId,1886 ) -> DispatchResult {1887 ensure!(1888 Self::is_owner_or_admin_permissions(collection, subject),1889 Error::<T>::NoPermission1890 );18911892 Ok(())1893 }18941895 fn owned_amount(1896 subject: &T::CrossAccountId,1897 target_collection: &CollectionHandle<T>,1898 item_id: TokenId,1899 ) -> Option<u128> {1900 let collection_id = target_collection.id;19011902 match target_collection.mode {1903 CollectionMode::NFT => {1904 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1905 }1906 CollectionMode::Fungible(_) => {1907 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1908 }1909 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1910 .owner1911 .iter()1912 .find(|i| i.owner == *subject)1913 .map(|i| i.fraction),1914 CollectionMode::Invalid => None,1915 }1916 }19171918 fn is_item_owner(1919 subject: &T::CrossAccountId,1920 target_collection: &CollectionHandle<T>,1921 item_id: TokenId,1922 ) -> bool {1923 match target_collection.mode {1924 CollectionMode::Fungible(_) => true,1925 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1926 }1927 }19281929 fn check_white_list(1930 collection: &CollectionHandle<T>,1931 address: &T::CrossAccountId,1932 ) -> DispatchResult {1933 let collection_id = collection.id;19341935 let mes = Error::<T>::AddresNotInWhiteList;1936 ensure!(1937 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1938 mes1939 );19401941 Ok(())1942 }19431944 1945 1946 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1947 let collection_id = target_collection.id;1948 let exists = match target_collection.mode {1949 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1950 CollectionMode::Fungible(_) => true,1951 CollectionMode::ReFungible => {1952 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1953 }1954 _ => false,1955 };19561957 ensure!(exists, Error::<T>::TokenNotFound);1958 Ok(())1959 }19601961 fn transfer_fungible(1962 collection: &CollectionHandle<T>,1963 value: u128,1964 owner: &T::CrossAccountId,1965 recipient: &T::CrossAccountId,1966 ) -> DispatchResult {1967 let collection_id = collection.id;19681969 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1970 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19711972 1973 Self::add_fungible_item(collection, recipient, value)?;19741975 1976 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19771978 1979 if balance.value == value {1980 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1981 } else {1982 balance.value -= value;1983 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1984 }19851986 collection.log(ERC20Events::Transfer {1987 from: *owner.as_eth(),1988 to: *recipient.as_eth(),1989 value: value.into(),1990 })?;1991 Self::deposit_event(RawEvent::Transfer(1992 collection.id,1993 1,1994 owner.clone(),1995 recipient.clone(),1996 value,1997 ));19981999 Ok(())2000 }20012002 fn transfer_refungible(2003 collection: &CollectionHandle<T>,2004 item_id: TokenId,2005 value: u128,2006 owner: T::CrossAccountId,2007 new_owner: T::CrossAccountId,2008 ) -> DispatchResult {2009 let collection_id = collection.id;2010 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2011 .ok_or(Error::<T>::TokenNotFound)?;20122013 let item = full_item2014 .owner2015 .iter()2016 .find(|i| i.owner == owner)2017 .ok_or(Error::<T>::TokenNotFound)?;2018 let amount = item.fraction;20192020 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20212022 2023 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2024 .checked_sub(value)2025 .ok_or(Error::<T>::NumOverflow)?;2026 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20272028 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2029 .checked_add(value)2030 .ok_or(Error::<T>::NumOverflow)?;2031 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20322033 let old_owner = item.owner.clone();2034 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20352036 let mut new_full_item = full_item.clone();2037 2038 if amount == value && !new_owner_has_account {2039 2040 2041 new_full_item2042 .owner2043 .iter_mut()2044 .find(|i| i.owner == owner)2045 .expect("old owner does present in refungible")2046 .owner = new_owner.clone();2047 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20482049 2050 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2051 } else {2052 new_full_item2053 .owner2054 .iter_mut()2055 .find(|i| i.owner == owner)2056 .expect("old owner does present in refungible")2057 .fraction -= value;20582059 2060 if new_owner_has_account {2061 2062 new_full_item2063 .owner2064 .iter_mut()2065 .find(|i| i.owner == new_owner)2066 .expect("new owner has account")2067 .fraction += value;2068 } else {2069 2070 new_full_item.owner.push(Ownership {2071 owner: new_owner.clone(),2072 fraction: value,2073 });2074 Self::add_token_index(collection_id, item_id, &new_owner)?;2075 }20762077 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2078 }20792080 Self::deposit_event(RawEvent::Transfer(2081 collection.id,2082 item_id,2083 owner,2084 new_owner,2085 amount,2086 ));20872088 Ok(())2089 }20902091 fn transfer_nft(2092 collection: &CollectionHandle<T>,2093 item_id: TokenId,2094 sender: T::CrossAccountId,2095 new_owner: T::CrossAccountId,2096 ) -> DispatchResult {2097 let collection_id = collection.id;2098 let mut item =2099 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21002101 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21022103 2104 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2105 .checked_sub(1)2106 .ok_or(Error::<T>::NumOverflow)?;2107 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21082109 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2110 .checked_add(1)2111 .ok_or(Error::<T>::NumOverflow)?;2112 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21132114 2115 let old_owner = item.owner.clone();2116 item.owner = new_owner.clone();2117 <NftItemList<T>>::insert(collection_id, item_id, item);21182119 2120 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21212122 collection.log(ERC721Events::Transfer {2123 from: *sender.as_eth(),2124 to: *new_owner.as_eth(),2125 token_id: item_id.into(),2126 })?;2127 Self::deposit_event(RawEvent::Transfer(2128 collection.id,2129 item_id,2130 sender,2131 new_owner,2132 1,2133 ));21342135 Ok(())2136 }21372138 fn set_re_fungible_variable_data(2139 collection: &CollectionHandle<T>,2140 item_id: TokenId,2141 data: Vec<u8>,2142 ) -> DispatchResult {2143 let collection_id = collection.id;2144 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2145 .ok_or(Error::<T>::TokenNotFound)?;21462147 item.variable_data = data;21482149 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21502151 Ok(())2152 }21532154 fn set_nft_variable_data(2155 collection: &CollectionHandle<T>,2156 item_id: TokenId,2157 data: Vec<u8>,2158 ) -> DispatchResult {2159 let collection_id = collection.id;2160 let mut item =2161 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21622163 item.variable_data = data;21642165 <NftItemList<T>>::insert(collection_id, item_id, item);21662167 Ok(())2168 }21692170 #[allow(dead_code)]2171 fn init_collection(item: &Collection<T>) {2172 2173 assert!(2174 item.decimal_points <= MAX_DECIMAL_POINTS,2175 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2176 );2177 assert!(2178 item.name.len() <= 64,2179 "Collection name can not be longer than 63 char"2180 );2181 assert!(2182 item.name.len() <= 256,2183 "Collection description can not be longer than 255 char"2184 );2185 assert!(2186 item.token_prefix.len() <= 16,2187 "Token prefix can not be longer than 15 char"2188 );21892190 2191 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21922193 CreatedCollectionCount::put(next_id);2194 }21952196 #[allow(dead_code)]2197 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2198 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21992200 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22012202 <ItemListIndex>::insert(collection_id, current_index);22032204 2205 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2206 .checked_add(1)2207 .unwrap();2208 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2209 }22102211 #[allow(dead_code)]2212 fn init_fungible_token(2213 collection_id: CollectionId,2214 owner: &T::CrossAccountId,2215 item: &FungibleItemType,2216 ) {2217 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22182219 Self::add_token_index(collection_id, current_index, owner).unwrap();22202221 <ItemListIndex>::insert(collection_id, current_index);22222223 2224 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2225 .checked_add(item.value)2226 .unwrap();2227 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2228 }22292230 #[allow(dead_code)]2231 fn init_refungible_token(2232 collection_id: CollectionId,2233 item: &ReFungibleItemType<T::CrossAccountId>,2234 ) {2235 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22362237 let value = item.owner.first().unwrap().fraction;2238 let owner = item.owner.first().unwrap().owner.clone();22392240 Self::add_token_index(collection_id, current_index, &owner).unwrap();22412242 <ItemListIndex>::insert(collection_id, current_index);22432244 2245 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2246 .checked_add(value)2247 .unwrap();2248 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2249 }22502251 fn add_token_index(2252 collection_id: CollectionId,2253 item_index: TokenId,2254 owner: &T::CrossAccountId,2255 ) -> DispatchResult {2256 2257 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2258 2259 let count = <AccountItemCount<T>>::get(owner.as_sub());2260 ensure!(2261 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2262 Error::<T>::AddressOwnershipLimitExceeded2263 );22642265 <AccountItemCount<T>>::insert(2266 owner.as_sub(),2267 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2268 );2269 } else {2270 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2271 }22722273 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2274 if list_exists {2275 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2276 let item_contains = list.contains(&item_index.clone());22772278 if !item_contains {2279 list.push(item_index);2280 }22812282 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2283 } else {2284 let itm = vec![item_index];2285 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2286 }22872288 Ok(())2289 }22902291 fn remove_token_index(2292 collection_id: CollectionId,2293 item_index: TokenId,2294 owner: &T::CrossAccountId,2295 ) -> DispatchResult {2296 2297 <AccountItemCount<T>>::insert(2298 owner.as_sub(),2299 <AccountItemCount<T>>::get(owner.as_sub())2300 .checked_sub(1)2301 .ok_or(Error::<T>::NumOverflow)?,2302 );23032304 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2305 if list_exists {2306 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2307 let item_contains = list.contains(&item_index.clone());23082309 if item_contains {2310 list.retain(|&item| item != item_index);2311 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2312 }2313 }23142315 Ok(())2316 }23172318 fn move_token_index(2319 collection_id: CollectionId,2320 item_index: TokenId,2321 old_owner: &T::CrossAccountId,2322 new_owner: &T::CrossAccountId,2323 ) -> DispatchResult {2324 Self::remove_token_index(collection_id, item_index, old_owner)?;2325 Self::add_token_index(collection_id, item_index, new_owner)?;23262327 Ok(())2328 }2329}23302331sp_api::decl_runtime_apis! {2332 pub trait NftApi {2333 2334 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2335 }2336}