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, 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 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 TotalCollectionsLimitExceeded,74 75 CollectionDecimalPointLimitExceeded,76 77 CollectionNameLimitExceeded,78 79 CollectionDescriptionLimitExceeded,80 81 CollectionTokenPrefixLimitExceeded,82 83 CollectionNotFound,84 85 TokenNotFound,86 87 AdminNotFound,88 89 NumOverflow,90 91 AlreadyAdmin,92 93 NoPermission,94 95 ConfirmUnsetSponsorFail,96 97 PublicMintingNotAllowed,98 99 MustBeTokenOwner,100 101 TokenValueTooLow,102 103 NftSizeLimitExceeded,104 105 ApproveNotFound,106 107 TokenValueNotEnough,108 109 ApproveRequired,110 111 AddresNotInWhiteList,112 113 CollectionAdminsLimitExceeded,114 115 AddressOwnershipLimitExceeded,116 117 EmptyArgument,118 119 TokenConstDataLimitExceeded,120 121 TokenVariableDataLimitExceeded,122 123 NotNftDataUsedToMintNftCollectionToken,124 125 NotFungibleDataUsedToMintFungibleCollectionToken,126 127 NotReFungibleDataUsedToMintReFungibleCollectionToken,128 129 UnexpectedCollectionType,130 131 CantStoreMetadataInFungibleTokens,132 133 CollectionTokenLimitExceeded,134 135 AccountTokenLimitExceeded,136 137 CollectionLimitBoundsExceeded,138 139 OwnerPermissionsCantBeReverted,140 141 SchemaDataLimitExceeded,142 143 WrongRefungiblePieces,144 145 BadCreateRefungibleCall,146 147 OutOfGas,148 149 TransferNotAllowed,150 }151}152153#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]154pub struct CollectionHandle<T: Config> {155 pub id: CollectionId,156 collection: Collection<T>,157 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,158}159impl<T: Config> CollectionHandle<T> {160 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {161 <CollectionById<T>>::get(id).map(|collection| Self {162 id,163 collection,164 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(165 eth::collection_id_to_address(id),166 gas_limit,167 ),168 })169 }170 pub fn get(id: CollectionId) -> Option<Self> {171 Self::get_with_gas_limit(id, u64::MAX)172 }173 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {174 self.recorder.log_sub(log)175 }176 fn consume_gas(&self, gas: u64) -> DispatchResult {177 self.recorder.consume_gas_sub(gas)178 }179 pub fn submit_logs(self) -> DispatchResult {180 self.recorder.submit_logs()181 }182 pub fn save(self) -> DispatchResult {183 self.recorder.submit_logs()?;184 <CollectionById<T>>::insert(self.id, self.collection);185 Ok(())186 }187}188impl<T: Config> Deref for CollectionHandle<T> {189 type Target = Collection<T>;190191 fn deref(&self) -> &Self::Target {192 &self.collection193 }194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197 fn deref_mut(&mut self) -> &mut Self::Target {198 &mut self.collection199 }200}201202pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {203 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;204205 206 type WeightInfo: WeightInfo;207208 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;209 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;210211 type CrossAccountId: CrossAccountId<Self::AccountId>;212 type Currency: Currency<Self::AccountId>;213 type CollectionCreationPrice: Get<214 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,215 >;216 type TreasuryAccountId: Get<Self::AccountId>;217}218219type SelfWeightOf<T> = <T as Config>::WeightInfo;220221trait WeightInfoHelpers: WeightInfo {222 fn transfer() -> Weight {223 Self::transfer_nft()224 .max(Self::transfer_fungible())225 .max(Self::transfer_refungible())226 }227 fn transfer_from() -> Weight {228 Self::transfer_from_nft()229 .max(Self::transfer_from_fungible())230 .max(Self::transfer_from_refungible())231 }232 fn approve() -> Weight {233 234 Self::approve_nft()235 }236 fn set_variable_meta_data(data: u32) -> Weight {237 238 Self::set_variable_meta_data_nft(data)239 }240 fn create_item(data: u32) -> Weight {241 Self::create_item_nft(data)242 .max(Self::create_item_fungible())243 .max(Self::create_item_refungible(data))244 }245 fn burn_item() -> Weight {246 247 Self::burn_item_nft()248 }249}250impl<T: WeightInfo> WeightInfoHelpers for T {}251252253254255256257258259260261262263264265266267268269270271272273274decl_storage! {275 trait Store for Module<T: Config> as Nft {276277 278 279 CreatedCollectionCount: u32;280 281 ChainVersion: u64;282 283 284 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;285 286287 288 289 290 DestroyedCollectionCount: u32;291 292 293 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294 295296 297 298 299 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300 301 302 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303 304 305 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306 307308 309 310 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312 313 314 315 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317 318 319 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320 321 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322 323 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324 325326 327 328 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329 330331 332 333 334 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335 336 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337 338 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339 340 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341 342343 344 345 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346 }347 add_extra_genesis {348 build(|config: &GenesisConfig<T>| {349 350 for (_num, _c) in &config.collection_id {351 <Module<T>>::init_collection(_c);352 }353354 for (_num, _c, _i) in &config.nft_item_id {355 <Module<T>>::init_nft_token(*_c, _i);356 }357358 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {359 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);360 }361362 for (_num, _c, _i) in &config.refungible_item_id {363 <Module<T>>::init_refungible_token(*_c, _i);364 }365 })366 }367}368369decl_event!(370 pub enum Event<T>371 where372 AccountId = <T as frame_system::Config>::AccountId,373 CrossAccountId = <T as Config>::CrossAccountId,374 {375 376 377 378 379 380 381 382 383 384 CollectionCreated(CollectionId, u8, AccountId),385386 387 388 389 390 391 392 393 394 395 ItemCreated(CollectionId, TokenId, CrossAccountId),396397 398 399 400 401 402 403 404 ItemDestroyed(CollectionId, TokenId),405406 407 408 409 410 411 412 413 414 415 416 417 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),418419 420 421 422 423 424 425 426 427 428 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),429 }430);431432decl_module! {433 pub struct Module<T: Config> for enum Call434 where435 origin: T::Origin436 {437 fn deposit_event() = default;438 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;439 type Error = Error<T>;440441 fn on_initialize(_now: T::BlockNumber) -> Weight {442 0443 }444445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 #[weight = <SelfWeightOf<T>>::create_collection()]462 #[transactional]463 pub fn create_collection(origin,464 collection_name: Vec<u16>,465 collection_description: Vec<u16>,466 token_prefix: Vec<u8>,467 mode: CollectionMode) -> DispatchResult {468469 470 let who = ensure_signed(origin)?;471472 473 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();474 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(475 &T::TreasuryAccountId::get(),476 T::CollectionCreationPrice::get(),477 ));478 <T as Config>::Currency::settle(479 &who,480 imbalance,481 WithdrawReasons::TRANSFER,482 ExistenceRequirement::KeepAlive,483 ).map_err(|_| Error::<T>::NoPermission)?;484485 let decimal_points = match mode {486 CollectionMode::Fungible(points) => points,487 _ => 0488 };489490 let created_count = CreatedCollectionCount::get();491 let destroyed_count = DestroyedCollectionCount::get();492493 494 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);495496 497 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);498 ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);499 ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);500 ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);501502 503 let next_id = created_count504 .checked_add(1)505 .ok_or(Error::<T>::NumOverflow)?;506507 CreatedCollectionCount::put(next_id);508509 let limits = CollectionLimits {510 sponsored_data_size: CUSTOM_DATA_LIMIT,511 ..Default::default()512 };513514 515 let new_collection = Collection {516 owner: who.clone(),517 name: collection_name,518 mode: mode.clone(),519 mint_mode: false,520 access: AccessMode::Normal,521 description: collection_description,522 decimal_points,523 token_prefix,524 offchain_schema: Vec::new(),525 schema_version: SchemaVersion::ImageURL,526 sponsorship: SponsorshipState::Disabled,527 variable_on_chain_schema: Vec::new(),528 const_on_chain_schema: Vec::new(),529 limits,530 transfers_enabled: true,531 };532533 534 <CollectionById<T>>::insert(next_id, new_collection);535536 537 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));538539 Ok(())540 }541542 543 544 545 546 547 548 549 550 551 #[weight = <SelfWeightOf<T>>::destroy_collection()]552 #[transactional]553 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {554555 let sender = ensure_signed(origin)?;556 let collection = Self::get_collection(collection_id)?;557 Self::check_owner_permissions(&collection, &sender)?;558 if !collection.limits.owner_can_destroy {559 fail!(Error::<T>::NoPermission);560 }561562 <AddressTokens<T>>::remove_prefix(collection_id, None);563 <Allowances<T>>::remove_prefix(collection_id, None);564 <Balance<T>>::remove_prefix(collection_id, None);565 <ItemListIndex>::remove(collection_id);566 <AdminList<T>>::remove(collection_id);567 <CollectionById<T>>::remove(collection_id);568 <WhiteList<T>>::remove_prefix(collection_id, None);569570 <NftItemList<T>>::remove_prefix(collection_id, None);571 <FungibleItemList<T>>::remove_prefix(collection_id, None);572 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);573574 <NftTransferBasket<T>>::remove_prefix(collection_id, None);575 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);576 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);577578 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);579580 DestroyedCollectionCount::put(DestroyedCollectionCount::get()581 .checked_add(1)582 .ok_or(Error::<T>::NumOverflow)?);583584 Ok(())585 }586587 588 589 590 591 592 593 594 595 596 597 598 599 #[weight = <SelfWeightOf<T>>::add_to_white_list()]600 #[transactional]601 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{602603 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);604 let collection = Self::get_collection(collection_id)?;605606 Self::toggle_white_list_internal(607 &sender,608 &collection,609 &address,610 true,611 )?;612613 Ok(())614 }615616 617 618 619 620 621 622 623 624 625 626 627 628 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]629 #[transactional]630 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{631632 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633 let collection = Self::get_collection(collection_id)?;634635 Self::toggle_white_list_internal(636 &sender,637 &collection,638 &address,639 false,640 )?;641642 Ok(())643 }644645 646 647 648 649 650 651 652 653 654 655 656 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]657 #[transactional]658 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult659 {660 let sender = ensure_signed(origin)?;661662 let mut target_collection = Self::get_collection(collection_id)?;663 Self::check_owner_permissions(&target_collection, &sender)?;664 target_collection.access = mode;665 target_collection.save()666 }667668 669 670 671 672 673 674 675 676 677 678 679 680 681 #[weight = <SelfWeightOf<T>>::set_mint_permission()]682 #[transactional]683 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult684 {685 let sender = ensure_signed(origin)?;686687 let mut target_collection = Self::get_collection(collection_id)?;688 Self::check_owner_permissions(&target_collection, &sender)?;689 target_collection.mint_mode = mint_permission;690 target_collection.save()691 }692693 694 695 696 697 698 699 700 701 702 703 704 #[weight = <SelfWeightOf<T>>::change_collection_owner()]705 #[transactional]706 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {707708 let sender = ensure_signed(origin)?;709 let mut target_collection = Self::get_collection(collection_id)?;710 Self::check_owner_permissions(&target_collection, &sender)?;711 target_collection.owner = new_owner;712 target_collection.save()713 }714715 716 717 718 719 720 721 722 723 724 725 726 727 728 #[weight = <SelfWeightOf<T>>::add_collection_admin()]729 #[transactional]730 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {731 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);732 let collection = Self::get_collection(collection_id)?;733 Self::check_owner_or_admin_permissions(&collection, &sender)?;734 let mut admin_arr = <AdminList<T>>::get(collection_id);735736 match admin_arr.binary_search(&new_admin_id) {737 Ok(_) => {},738 Err(idx) => {739 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);740 admin_arr.insert(idx, new_admin_id);741 <AdminList<T>>::insert(collection_id, admin_arr);742 }743 }744 Ok(())745 }746747 748 749 750 751 752 753 754 755 756 757 758 759 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]760 #[transactional]761 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {762 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763 let collection = Self::get_collection(collection_id)?;764 Self::check_owner_or_admin_permissions(&collection, &sender)?;765 let mut admin_arr = <AdminList<T>>::get(collection_id);766767 if let Ok(idx) = admin_arr.binary_search(&account_id) {768 admin_arr.remove(idx);769 <AdminList<T>>::insert(collection_id, admin_arr);770 }771 Ok(())772 }773774 775 776 777 778 779 780 781 782 783 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]784 #[transactional]785 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {786 let sender = ensure_signed(origin)?;787 let mut target_collection = Self::get_collection(collection_id)?;788 Self::check_owner_permissions(&target_collection, &sender)?;789790 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);791 target_collection.save()792 }793794 795 796 797 798 799 800 801 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]802 #[transactional]803 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {804 let sender = ensure_signed(origin)?;805806 let mut target_collection = Self::get_collection(collection_id)?;807 ensure!(808 target_collection.sponsorship.pending_sponsor() == Some(&sender),809 Error::<T>::ConfirmUnsetSponsorFail810 );811812 target_collection.sponsorship = SponsorshipState::Confirmed(sender);813 target_collection.save()814 }815816 817 818 819 820 821 822 823 824 825 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]826 #[transactional]827 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {828 let sender = ensure_signed(origin)?;829830 let mut target_collection = Self::get_collection(collection_id)?;831 Self::check_owner_permissions(&target_collection, &sender)?;832833 target_collection.sponsorship = SponsorshipState::Disabled;834 target_collection.save()835 }836837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860861 #[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]862 #[transactional]863 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {864 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);865 let collection = Self::get_collection(collection_id)?;866867 Self::create_item_internal(&sender, &collection, &owner, data)?;868869 collection.submit_logs()870 }871872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 #[weight = <SelfWeightOf<T>>::create_item(items_data.iter()891 .map(|data| { data.data_size() as u32 })892 .sum())]893 #[transactional]894 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {895896 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);897 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);898 let collection = Self::get_collection(collection_id)?;899900 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;901902 collection.submit_logs()903 }904905 906907 908 909 910 911 912 913 914 915 916 917 918 #[weight = <SelfWeightOf<T>>::burn_item()]919 #[transactional]920 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {921922 let sender = ensure_signed(origin)?;923 let mut target_collection = Self::get_collection(collection_id)?;924925 Self::check_owner_permissions(&target_collection, &sender)?;926927 target_collection.transfers_enabled = value;928 target_collection.save()929 }930931 932 933 934 935 936 937 938 939 940 941 942 943 944 #[weight = <SelfWeightOf<T>>::burn_item()]945 #[transactional]946 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {947948 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);949 let target_collection = Self::get_collection(collection_id)?;950951 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;952953 target_collection.submit_logs()954 }955956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 #[weight = <SelfWeightOf<T>>::transfer()]980 #[transactional]981 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {982 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);983 let collection = Self::get_collection(collection_id)?;984985 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;986987 collection.submit_logs()988 }989990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 #[weight = <SelfWeightOf<T>>::approve()]1006 #[transactional]1007 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1008 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1009 let collection = Self::get_collection(collection_id)?;10101011 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10121013 collection.submit_logs()1014 }10151016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 #[weight = <SelfWeightOf<T>>::transfer_from()]1036 #[transactional]1037 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1038 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039 let collection = Self::get_collection(collection_id)?;10401041 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10421043 collection.submit_logs()1044 }1045 1046 1047 1048 1049 10501051 10521053 10541055 1056 10571058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1071 #[transactional]1072 pub fn set_variable_meta_data (1073 origin,1074 collection_id: CollectionId,1075 item_id: TokenId,1076 data: Vec<u8>1077 ) -> DispatchResult {1078 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10791080 let collection = Self::get_collection(collection_id)?;10811082 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10831084 Ok(())1085 }10861087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 #[weight = <SelfWeightOf<T>>::set_schema_version()]1102 #[transactional]1103 pub fn set_schema_version(1104 origin,1105 collection_id: CollectionId,1106 version: SchemaVersion1107 ) -> DispatchResult {1108 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1109 let mut target_collection = Self::get_collection(collection_id)?;1110 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1111 target_collection.schema_version = version;1112 target_collection.save()1113 }11141115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1128 #[transactional]1129 pub fn set_offchain_schema(1130 origin,1131 collection_id: CollectionId,1132 schema: Vec<u8>1133 ) -> DispatchResult {1134 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1135 let mut target_collection = Self::get_collection(collection_id)?;1136 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11371138 1139 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11401141 target_collection.offchain_schema = schema;1142 target_collection.save()1143 }11441145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1158 #[transactional]1159 pub fn set_const_on_chain_schema (1160 origin,1161 collection_id: CollectionId,1162 schema: Vec<u8>1163 ) -> DispatchResult {1164 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1165 let mut target_collection = Self::get_collection(collection_id)?;1166 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11671168 1169 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11701171 target_collection.const_on_chain_schema = schema;1172 target_collection.save()1173 }11741175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1188 #[transactional]1189 pub fn set_variable_on_chain_schema (1190 origin,1191 collection_id: CollectionId,1192 schema: Vec<u8>1193 ) -> DispatchResult {1194 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1195 let mut target_collection = Self::get_collection(collection_id)?;1196 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11971198 1199 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12001201 target_collection.variable_on_chain_schema = schema;1202 target_collection.save()1203 }12041205 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1206 #[transactional]1207 pub fn set_collection_limits(1208 origin,1209 collection_id: u32,1210 new_limits: CollectionLimits<T::BlockNumber>,1211 ) -> DispatchResult {1212 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1213 let mut target_collection = Self::get_collection(collection_id)?;1214 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1215 let old_limits = &target_collection.limits;12161217 1218 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1219 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1220 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1221 Error::<T>::CollectionLimitBoundsExceeded);12221223 1224 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1225 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12261227 ensure!(1228 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1229 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1230 Error::<T>::OwnerPermissionsCantBeReverted,1231 );12321233 target_collection.limits = new_limits;12341235 target_collection.save()1236 }1237 }1238}12391240impl<T: Config> Module<T> {1241 pub fn create_item_internal(1242 sender: &T::CrossAccountId,1243 collection: &CollectionHandle<T>,1244 owner: &T::CrossAccountId,1245 data: CreateItemData,1246 ) -> DispatchResult {1247 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1248 Self::validate_create_item_args(collection, &data)?;1249 Self::create_item_no_validation(collection, owner, data)?;12501251 Ok(())1252 }12531254 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 target_collection.consume_gas(2000000)?;1262 1263 Self::is_correct_transfer(target_collection, recipient)?;12641265 1266 ensure!(1267 Self::is_item_owner(sender, target_collection, item_id)1268 || Self::is_owner_or_admin_permissions(target_collection, sender),1269 Error::<T>::NoPermission1270 );12711272 if target_collection.access == AccessMode::WhiteList {1273 Self::check_white_list(target_collection, sender)?;1274 Self::check_white_list(target_collection, recipient)?;1275 }12761277 match target_collection.mode {1278 CollectionMode::NFT => Self::transfer_nft(1279 target_collection,1280 item_id,1281 sender.clone(),1282 recipient.clone(),1283 )?,1284 CollectionMode::Fungible(_) => {1285 Self::transfer_fungible(target_collection, value, sender, recipient)?1286 }1287 CollectionMode::ReFungible => Self::transfer_refungible(1288 target_collection,1289 item_id,1290 value,1291 sender.clone(),1292 recipient.clone(),1293 )?,1294 _ => (),1295 };12961297 Self::deposit_event(RawEvent::Transfer(1298 target_collection.id,1299 item_id,1300 sender.clone(),1301 recipient.clone(),1302 value,1303 ));13041305 Ok(())1306 }13071308 pub fn approve_internal(1309 sender: &T::CrossAccountId,1310 spender: &T::CrossAccountId,1311 collection: &CollectionHandle<T>,1312 item_id: TokenId,1313 amount: u128,1314 ) -> DispatchResult {1315 collection.consume_gas(2000000)?;1316 Self::token_exists(collection, item_id)?;13171318 1319 let bypasses_limits = collection.limits.owner_can_transfer1320 && Self::is_owner_or_admin_permissions(collection, sender);13211322 let allowance_limit = if bypasses_limits {1323 None1324 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1325 Some(amount)1326 } else {1327 fail!(Error::<T>::NoPermission);1328 };13291330 if collection.access == AccessMode::WhiteList {1331 Self::check_white_list(collection, sender)?;1332 Self::check_white_list(collection, spender)?;1333 }13341335 let allowance: u128 = amount1336 .checked_add(<Allowances<T>>::get(1337 collection.id,1338 (item_id, sender.as_sub(), spender.as_sub()),1339 ))1340 .ok_or(Error::<T>::NumOverflow)?;1341 if let Some(limit) = allowance_limit {1342 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1343 }1344 <Allowances<T>>::insert(1345 collection.id,1346 (item_id, sender.as_sub(), spender.as_sub()),1347 allowance,1348 );13491350 if matches!(collection.mode, CollectionMode::NFT) {1351 1352 collection.log(ERC721Events::Approval {1353 owner: *sender.as_eth(),1354 approved: *spender.as_eth(),1355 token_id: item_id.into(),1356 })?;1357 }13581359 if matches!(collection.mode, CollectionMode::Fungible(_)) {1360 1361 collection.log(ERC20Events::Approval {1362 owner: *sender.as_eth(),1363 spender: *spender.as_eth(),1364 value: allowance.into(),1365 })?;1366 }13671368 Self::deposit_event(RawEvent::Approved(1369 collection.id,1370 item_id,1371 sender.clone(),1372 spender.clone(),1373 allowance,1374 ));1375 Ok(())1376 }13771378 pub fn transfer_from_internal(1379 sender: &T::CrossAccountId,1380 from: &T::CrossAccountId,1381 recipient: &T::CrossAccountId,1382 collection: &CollectionHandle<T>,1383 item_id: TokenId,1384 amount: u128,1385 ) -> DispatchResult {1386 collection.consume_gas(2000000)?;1387 1388 let approval: u128 =1389 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13901391 1392 Self::is_correct_transfer(collection, recipient)?;13931394 1395 ensure!(1396 approval >= amount1397 || (collection.limits.owner_can_transfer1398 && Self::is_owner_or_admin_permissions(collection, sender)),1399 Error::<T>::NoPermission1400 );14011402 if collection.access == AccessMode::WhiteList {1403 Self::check_white_list(collection, sender)?;1404 Self::check_white_list(collection, recipient)?;1405 }14061407 1408 let allowance = approval.saturating_sub(amount);1409 if allowance > 0 {1410 <Allowances<T>>::insert(1411 collection.id,1412 (item_id, from.as_sub(), sender.as_sub()),1413 allowance,1414 );1415 } else {1416 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1417 }14181419 match collection.mode {1420 CollectionMode::NFT => {1421 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1422 }1423 CollectionMode::Fungible(_) => {1424 Self::transfer_fungible(collection, amount, from, recipient)?1425 }1426 CollectionMode::ReFungible => Self::transfer_refungible(1427 collection,1428 item_id,1429 amount,1430 from.clone(),1431 recipient.clone(),1432 )?,1433 _ => (),1434 };14351436 if matches!(collection.mode, CollectionMode::Fungible(_)) {1437 collection.log(ERC20Events::Approval {1438 owner: *from.as_eth(),1439 spender: *sender.as_eth(),1440 value: allowance.into(),1441 })?;1442 }14431444 Ok(())1445 }14461447 pub fn set_variable_meta_data_internal(1448 sender: &T::CrossAccountId,1449 collection: &CollectionHandle<T>,1450 item_id: TokenId,1451 data: Vec<u8>,1452 ) -> DispatchResult {1453 Self::token_exists(collection, item_id)?;14541455 ensure!(1456 CUSTOM_DATA_LIMIT >= data.len() as u32,1457 Error::<T>::TokenVariableDataLimitExceeded1458 );14591460 1461 ensure!(1462 Self::is_item_owner(sender, collection, item_id)1463 || Self::is_owner_or_admin_permissions(collection, sender),1464 Error::<T>::NoPermission1465 );14661467 match collection.mode {1468 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1469 CollectionMode::ReFungible => {1470 Self::set_re_fungible_variable_data(collection, item_id, data)?1471 }1472 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1473 _ => fail!(Error::<T>::UnexpectedCollectionType),1474 };14751476 Ok(())1477 }14781479 pub fn create_multiple_items_internal(1480 sender: &T::CrossAccountId,1481 collection: &CollectionHandle<T>,1482 owner: &T::CrossAccountId,1483 items_data: Vec<CreateItemData>,1484 ) -> DispatchResult {1485 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14861487 for data in &items_data {1488 Self::validate_create_item_args(collection, data)?;1489 }1490 for data in &items_data {1491 Self::create_item_no_validation(collection, owner, data.clone())?;1492 }14931494 Ok(())1495 }14961497 pub fn burn_item_internal(1498 sender: &T::CrossAccountId,1499 collection: &CollectionHandle<T>,1500 item_id: TokenId,1501 value: u128,1502 ) -> DispatchResult {1503 ensure!(1504 Self::is_item_owner(sender, collection, item_id)1505 || (collection.limits.owner_can_transfer1506 && Self::is_owner_or_admin_permissions(collection, sender)),1507 Error::<T>::NoPermission1508 );15091510 if collection.access == AccessMode::WhiteList {1511 Self::check_white_list(collection, sender)?;1512 }15131514 match collection.mode {1515 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1516 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1517 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1518 _ => (),1519 };15201521 Ok(())1522 }15231524 pub fn toggle_white_list_internal(1525 sender: &T::CrossAccountId,1526 collection: &CollectionHandle<T>,1527 address: &T::CrossAccountId,1528 whitelisted: bool,1529 ) -> DispatchResult {1530 Self::check_owner_or_admin_permissions(collection, sender)?;15311532 if whitelisted {1533 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1534 } else {1535 <WhiteList<T>>::remove(collection.id, address.as_sub());1536 }15371538 Ok(())1539 }15401541 fn is_correct_transfer(1542 collection: &CollectionHandle<T>,1543 recipient: &T::CrossAccountId,1544 ) -> DispatchResult {1545 let collection_id = collection.id;15461547 1548 let account_items: u32 =1549 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1550 ensure!(1551 collection.limits.account_token_ownership_limit > account_items,1552 Error::<T>::AccountTokenLimitExceeded1553 );15541555 1556 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15571558 Ok(())1559 }15601561 fn can_create_items_in_collection(1562 collection: &CollectionHandle<T>,1563 sender: &T::CrossAccountId,1564 owner: &T::CrossAccountId,1565 amount: u32,1566 ) -> DispatchResult {1567 let collection_id = collection.id;15681569 1570 let total_items: u32 = ItemListIndex::get(collection_id)1571 .checked_add(amount)1572 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1573 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1574 as u32)1575 .checked_add(amount)1576 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1577 ensure!(1578 collection.limits.token_limit >= total_items,1579 Error::<T>::CollectionTokenLimitExceeded1580 );1581 ensure!(1582 collection.limits.account_token_ownership_limit >= account_items,1583 Error::<T>::AccountTokenLimitExceeded1584 );15851586 if !Self::is_owner_or_admin_permissions(collection, sender) {1587 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1588 Self::check_white_list(collection, owner)?;1589 Self::check_white_list(collection, sender)?;1590 }15911592 Ok(())1593 }15941595 fn validate_create_item_args(1596 target_collection: &CollectionHandle<T>,1597 data: &CreateItemData,1598 ) -> DispatchResult {1599 match target_collection.mode {1600 CollectionMode::NFT => {1601 if !matches!(data, CreateItemData::NFT(_)) {1602 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1603 }1604 }1605 CollectionMode::Fungible(_) => {1606 if !matches!(data, CreateItemData::Fungible(_)) {1607 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1608 }1609 }1610 CollectionMode::ReFungible => {1611 if let CreateItemData::ReFungible(data) = data {1612 1613 ensure!(1614 data.pieces <= MAX_REFUNGIBLE_PIECES,1615 Error::<T>::WrongRefungiblePieces1616 );1617 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1618 } else {1619 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1620 }1621 }1622 _ => {1623 fail!(Error::<T>::UnexpectedCollectionType);1624 }1625 };16261627 Ok(())1628 }16291630 fn create_item_no_validation(1631 collection: &CollectionHandle<T>,1632 owner: &T::CrossAccountId,1633 data: CreateItemData,1634 ) -> DispatchResult {1635 match data {1636 CreateItemData::NFT(data) => {1637 let item = NftItemType {1638 owner: owner.clone(),1639 const_data: data.const_data.into_inner(),1640 variable_data: data.variable_data.into_inner(),1641 };16421643 Self::add_nft_item(collection, item)?;1644 }1645 CreateItemData::Fungible(data) => {1646 Self::add_fungible_item(collection, owner, data.value)?;1647 }1648 CreateItemData::ReFungible(data) => {1649 let owner_list = vec![Ownership {1650 owner: owner.clone(),1651 fraction: data.pieces,1652 }];16531654 let item = ReFungibleItemType {1655 owner: owner_list,1656 const_data: data.const_data.into_inner(),1657 variable_data: data.variable_data.into_inner(),1658 };16591660 Self::add_refungible_item(collection, item)?;1661 }1662 };16631664 Ok(())1665 }16661667 fn add_fungible_item(1668 collection: &CollectionHandle<T>,1669 owner: &T::CrossAccountId,1670 value: u128,1671 ) -> DispatchResult {1672 let collection_id = collection.id;16731674 1675 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16761677 1678 let item = FungibleItemType {1679 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1680 };1681 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16821683 1684 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1685 .checked_add(value)1686 .ok_or(Error::<T>::NumOverflow)?;1687 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16881689 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1690 Ok(())1691 }16921693 fn add_refungible_item(1694 collection: &CollectionHandle<T>,1695 item: ReFungibleItemType<T::CrossAccountId>,1696 ) -> DispatchResult {1697 let collection_id = collection.id;16981699 let current_index = <ItemListIndex>::get(collection_id)1700 .checked_add(1)1701 .ok_or(Error::<T>::NumOverflow)?;1702 let itemcopy = item.clone();17031704 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1705 let item_owner = item.owner.first().expect("only one owner is defined");17061707 let value = item_owner.fraction;1708 let owner = item_owner.owner.clone();17091710 Self::add_token_index(collection_id, current_index, &owner)?;17111712 <ItemListIndex>::insert(collection_id, current_index);1713 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17141715 1716 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1717 .checked_add(value)1718 .ok_or(Error::<T>::NumOverflow)?;1719 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17201721 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1722 Ok(())1723 }17241725 fn add_nft_item(1726 collection: &CollectionHandle<T>,1727 item: NftItemType<T::CrossAccountId>,1728 ) -> DispatchResult {1729 let collection_id = collection.id;17301731 let current_index = <ItemListIndex>::get(collection_id)1732 .checked_add(1)1733 .ok_or(Error::<T>::NumOverflow)?;17341735 let item_owner = item.owner.clone();1736 Self::add_token_index(collection_id, current_index, &item.owner)?;17371738 <ItemListIndex>::insert(collection_id, current_index);1739 <NftItemList<T>>::insert(collection_id, current_index, item);17401741 1742 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1743 .checked_add(1)1744 .ok_or(Error::<T>::NumOverflow)?;1745 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17461747 collection.log(ERC721Events::Transfer {1748 from: H160::default(),1749 to: *item_owner.as_eth(),1750 token_id: current_index.into(),1751 })?;1752 Self::deposit_event(RawEvent::ItemCreated(1753 collection_id,1754 current_index,1755 item_owner,1756 ));1757 Ok(())1758 }17591760 fn burn_refungible_item(1761 collection: &CollectionHandle<T>,1762 item_id: TokenId,1763 owner: &T::CrossAccountId,1764 ) -> DispatchResult {1765 let collection_id = collection.id;17661767 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1768 .ok_or(Error::<T>::TokenNotFound)?;1769 let rft_balance = token1770 .owner1771 .iter()1772 .find(|&i| i.owner == *owner)1773 .ok_or(Error::<T>::TokenNotFound)?;1774 Self::remove_token_index(collection_id, item_id, owner)?;17751776 1777 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1778 .checked_sub(rft_balance.fraction)1779 .ok_or(Error::<T>::NumOverflow)?;1780 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17811782 1783 let index = token1784 .owner1785 .iter()1786 .position(|i| i.owner == *owner)1787 .expect("owned item is exists");1788 token.owner.remove(index);1789 let owner_count = token.owner.len();17901791 1792 if owner_count == 0 {1793 <ReFungibleItemList<T>>::remove(collection_id, item_id);1794 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1795 } else {1796 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1797 }17981799 Ok(())1800 }18011802 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1803 let collection_id = collection.id;18041805 let item =1806 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1807 Self::remove_token_index(collection_id, item_id, &item.owner)?;18081809 1810 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1811 .checked_sub(1)1812 .ok_or(Error::<T>::NumOverflow)?;1813 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1814 <NftItemList<T>>::remove(collection_id, item_id);1815 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18161817 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1818 Ok(())1819 }18201821 fn burn_fungible_item(1822 owner: &T::CrossAccountId,1823 collection: &CollectionHandle<T>,1824 value: u128,1825 ) -> DispatchResult {1826 let collection_id = collection.id;18271828 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1829 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18301831 1832 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1833 .checked_sub(value)1834 .ok_or(Error::<T>::NumOverflow)?;1835 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18361837 if balance.value - value > 0 {1838 balance.value -= value;1839 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1840 } else {1841 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1842 }18431844 collection.log(ERC20Events::Transfer {1845 from: *owner.as_eth(),1846 to: H160::default(),1847 value: value.into(),1848 })?;1849 Ok(())1850 }18511852 pub fn get_collection(1853 collection_id: CollectionId,1854 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1855 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1856 }18571858 fn check_owner_permissions(1859 target_collection: &CollectionHandle<T>,1860 subject: &T::AccountId,1861 ) -> DispatchResult {1862 ensure!(1863 *subject == target_collection.owner,1864 Error::<T>::NoPermission1865 );18661867 Ok(())1868 }18691870 fn is_owner_or_admin_permissions(1871 collection: &CollectionHandle<T>,1872 subject: &T::CrossAccountId,1873 ) -> bool {1874 *subject.as_sub() == collection.owner1875 || <AdminList<T>>::get(collection.id).contains(subject)1876 }18771878 fn check_owner_or_admin_permissions(1879 collection: &CollectionHandle<T>,1880 subject: &T::CrossAccountId,1881 ) -> DispatchResult {1882 ensure!(1883 Self::is_owner_or_admin_permissions(collection, subject),1884 Error::<T>::NoPermission1885 );18861887 Ok(())1888 }18891890 fn owned_amount(1891 subject: &T::CrossAccountId,1892 target_collection: &CollectionHandle<T>,1893 item_id: TokenId,1894 ) -> Option<u128> {1895 let collection_id = target_collection.id;18961897 match target_collection.mode {1898 CollectionMode::NFT => {1899 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1900 }1901 CollectionMode::Fungible(_) => {1902 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1903 }1904 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1905 .owner1906 .iter()1907 .find(|i| i.owner == *subject)1908 .map(|i| i.fraction),1909 CollectionMode::Invalid => None,1910 }1911 }19121913 fn is_item_owner(1914 subject: &T::CrossAccountId,1915 target_collection: &CollectionHandle<T>,1916 item_id: TokenId,1917 ) -> bool {1918 match target_collection.mode {1919 CollectionMode::Fungible(_) => true,1920 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1921 }1922 }19231924 fn check_white_list(1925 collection: &CollectionHandle<T>,1926 address: &T::CrossAccountId,1927 ) -> DispatchResult {1928 let collection_id = collection.id;19291930 let mes = Error::<T>::AddresNotInWhiteList;1931 ensure!(1932 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1933 mes1934 );19351936 Ok(())1937 }19381939 1940 1941 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1942 let collection_id = target_collection.id;1943 let exists = match target_collection.mode {1944 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1945 CollectionMode::Fungible(_) => true,1946 CollectionMode::ReFungible => {1947 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1948 }1949 _ => false,1950 };19511952 ensure!(exists, Error::<T>::TokenNotFound);1953 Ok(())1954 }19551956 fn transfer_fungible(1957 collection: &CollectionHandle<T>,1958 value: u128,1959 owner: &T::CrossAccountId,1960 recipient: &T::CrossAccountId,1961 ) -> DispatchResult {1962 let collection_id = collection.id;19631964 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1965 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19661967 1968 Self::add_fungible_item(collection, recipient, value)?;19691970 1971 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19721973 1974 if balance.value == value {1975 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1976 } else {1977 balance.value -= value;1978 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1979 }19801981 collection.log(ERC20Events::Transfer {1982 from: *owner.as_eth(),1983 to: *recipient.as_eth(),1984 value: value.into(),1985 })?;1986 Self::deposit_event(RawEvent::Transfer(1987 collection.id,1988 1,1989 owner.clone(),1990 recipient.clone(),1991 value,1992 ));19931994 Ok(())1995 }19961997 fn transfer_refungible(1998 collection: &CollectionHandle<T>,1999 item_id: TokenId,2000 value: u128,2001 owner: T::CrossAccountId,2002 new_owner: T::CrossAccountId,2003 ) -> DispatchResult {2004 let collection_id = collection.id;2005 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2006 .ok_or(Error::<T>::TokenNotFound)?;20072008 let item = full_item2009 .owner2010 .iter()2011 .find(|i| i.owner == owner)2012 .ok_or(Error::<T>::TokenNotFound)?;2013 let amount = item.fraction;20142015 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20162017 2018 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2019 .checked_sub(value)2020 .ok_or(Error::<T>::NumOverflow)?;2021 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20222023 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2024 .checked_add(value)2025 .ok_or(Error::<T>::NumOverflow)?;2026 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20272028 let old_owner = item.owner.clone();2029 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20302031 let mut new_full_item = full_item.clone();2032 2033 if amount == value && !new_owner_has_account {2034 2035 2036 new_full_item2037 .owner2038 .iter_mut()2039 .find(|i| i.owner == owner)2040 .expect("old owner does present in refungible")2041 .owner = new_owner.clone();2042 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20432044 2045 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2046 } else {2047 new_full_item2048 .owner2049 .iter_mut()2050 .find(|i| i.owner == owner)2051 .expect("old owner does present in refungible")2052 .fraction -= value;20532054 2055 if new_owner_has_account {2056 2057 new_full_item2058 .owner2059 .iter_mut()2060 .find(|i| i.owner == new_owner)2061 .expect("new owner has account")2062 .fraction += value;2063 } else {2064 2065 new_full_item.owner.push(Ownership {2066 owner: new_owner.clone(),2067 fraction: value,2068 });2069 Self::add_token_index(collection_id, item_id, &new_owner)?;2070 }20712072 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2073 }20742075 Self::deposit_event(RawEvent::Transfer(2076 collection.id,2077 item_id,2078 owner,2079 new_owner,2080 amount,2081 ));20822083 Ok(())2084 }20852086 fn transfer_nft(2087 collection: &CollectionHandle<T>,2088 item_id: TokenId,2089 sender: T::CrossAccountId,2090 new_owner: T::CrossAccountId,2091 ) -> DispatchResult {2092 let collection_id = collection.id;2093 let mut item =2094 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20952096 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20972098 2099 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2100 .checked_sub(1)2101 .ok_or(Error::<T>::NumOverflow)?;2102 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21032104 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2105 .checked_add(1)2106 .ok_or(Error::<T>::NumOverflow)?;2107 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21082109 2110 let old_owner = item.owner.clone();2111 item.owner = new_owner.clone();2112 <NftItemList<T>>::insert(collection_id, item_id, item);21132114 2115 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21162117 collection.log(ERC721Events::Transfer {2118 from: *sender.as_eth(),2119 to: *new_owner.as_eth(),2120 token_id: item_id.into(),2121 })?;2122 Self::deposit_event(RawEvent::Transfer(2123 collection.id,2124 item_id,2125 sender,2126 new_owner,2127 1,2128 ));21292130 Ok(())2131 }21322133 fn set_re_fungible_variable_data(2134 collection: &CollectionHandle<T>,2135 item_id: TokenId,2136 data: Vec<u8>,2137 ) -> DispatchResult {2138 let collection_id = collection.id;2139 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2140 .ok_or(Error::<T>::TokenNotFound)?;21412142 item.variable_data = data;21432144 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21452146 Ok(())2147 }21482149 fn set_nft_variable_data(2150 collection: &CollectionHandle<T>,2151 item_id: TokenId,2152 data: Vec<u8>,2153 ) -> DispatchResult {2154 let collection_id = collection.id;2155 let mut item =2156 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21572158 item.variable_data = data;21592160 <NftItemList<T>>::insert(collection_id, item_id, item);21612162 Ok(())2163 }21642165 #[allow(dead_code)]2166 fn init_collection(item: &Collection<T>) {2167 2168 assert!(2169 item.decimal_points <= MAX_DECIMAL_POINTS,2170 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2171 );2172 assert!(2173 item.name.len() <= 64,2174 "Collection name can not be longer than 63 char"2175 );2176 assert!(2177 item.name.len() <= 256,2178 "Collection description can not be longer than 255 char"2179 );2180 assert!(2181 item.token_prefix.len() <= 16,2182 "Token prefix can not be longer than 15 char"2183 );21842185 2186 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21872188 CreatedCollectionCount::put(next_id);2189 }21902191 #[allow(dead_code)]2192 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2193 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21942195 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21962197 <ItemListIndex>::insert(collection_id, current_index);21982199 2200 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2201 .checked_add(1)2202 .unwrap();2203 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2204 }22052206 #[allow(dead_code)]2207 fn init_fungible_token(2208 collection_id: CollectionId,2209 owner: &T::CrossAccountId,2210 item: &FungibleItemType,2211 ) {2212 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22132214 Self::add_token_index(collection_id, current_index, owner).unwrap();22152216 <ItemListIndex>::insert(collection_id, current_index);22172218 2219 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2220 .checked_add(item.value)2221 .unwrap();2222 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2223 }22242225 #[allow(dead_code)]2226 fn init_refungible_token(2227 collection_id: CollectionId,2228 item: &ReFungibleItemType<T::CrossAccountId>,2229 ) {2230 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22312232 let value = item.owner.first().unwrap().fraction;2233 let owner = item.owner.first().unwrap().owner.clone();22342235 Self::add_token_index(collection_id, current_index, &owner).unwrap();22362237 <ItemListIndex>::insert(collection_id, current_index);22382239 2240 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2241 .checked_add(value)2242 .unwrap();2243 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2244 }22452246 fn add_token_index(2247 collection_id: CollectionId,2248 item_index: TokenId,2249 owner: &T::CrossAccountId,2250 ) -> DispatchResult {2251 2252 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2253 2254 let count = <AccountItemCount<T>>::get(owner.as_sub());2255 ensure!(2256 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2257 Error::<T>::AddressOwnershipLimitExceeded2258 );22592260 <AccountItemCount<T>>::insert(2261 owner.as_sub(),2262 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2263 );2264 } else {2265 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2266 }22672268 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2269 if list_exists {2270 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2271 let item_contains = list.contains(&item_index.clone());22722273 if !item_contains {2274 list.push(item_index);2275 }22762277 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2278 } else {2279 let itm = vec![item_index];2280 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2281 }22822283 Ok(())2284 }22852286 fn remove_token_index(2287 collection_id: CollectionId,2288 item_index: TokenId,2289 owner: &T::CrossAccountId,2290 ) -> DispatchResult {2291 2292 <AccountItemCount<T>>::insert(2293 owner.as_sub(),2294 <AccountItemCount<T>>::get(owner.as_sub())2295 .checked_sub(1)2296 .ok_or(Error::<T>::NumOverflow)?,2297 );22982299 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2300 if list_exists {2301 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2302 let item_contains = list.contains(&item_index.clone());23032304 if item_contains {2305 list.retain(|&item| item != item_index);2306 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2307 }2308 }23092310 Ok(())2311 }23122313 fn move_token_index(2314 collection_id: CollectionId,2315 item_index: TokenId,2316 old_owner: &T::CrossAccountId,2317 new_owner: &T::CrossAccountId,2318 ) -> DispatchResult {2319 Self::remove_token_index(collection_id, item_index, old_owner)?;2320 Self::add_token_index(collection_id, item_index, new_owner)?;23212322 Ok(())2323 }2324}23252326sp_api::decl_runtime_apis! {2327 pub trait NftApi {2328 2329 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2330 }2331}