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}182183#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]184pub struct CollectionHandle<T: Config> {185 pub id: CollectionId,186 collection: Collection<T>,187 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,188}189impl<T: Config> CollectionHandle<T> {190 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {191 <CollectionById<T>>::get(id).map(|collection| Self {192 id,193 collection,194 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(195 eth::collection_id_to_address(id),196 gas_limit,197 ),198 })199 }200 pub fn get(id: CollectionId) -> Option<Self> {201 Self::get_with_gas_limit(id, u64::MAX)202 }203 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {204 self.recorder.log_sub(log)205 }206 fn consume_gas(&self, gas: u64) -> DispatchResult {207 self.recorder.consume_gas_sub(gas)208 }209 pub fn submit_logs(self) -> DispatchResult {210 self.recorder.submit_logs()211 }212 pub fn save(self) -> DispatchResult {213 self.recorder.submit_logs()?;214 <CollectionById<T>>::insert(self.id, self.collection);215 Ok(())216 }217}218impl<T: Config> Deref for CollectionHandle<T> {219 type Target = Collection<T>;220221 fn deref(&self) -> &Self::Target {222 &self.collection223 }224}225226impl<T: Config> DerefMut for CollectionHandle<T> {227 fn deref_mut(&mut self) -> &mut Self::Target {228 &mut self.collection229 }230}231232pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {233 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;234235 236 type WeightInfo: WeightInfo;237238 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;239 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;240241 type CrossAccountId: CrossAccountId<Self::AccountId>;242 type Currency: Currency<Self::AccountId>;243 type CollectionCreationPrice: Get<244 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,245 >;246 type TreasuryAccountId: Get<Self::AccountId>;247}248249250251252253254255256257258259260261262263264265266267268269270271decl_storage! {272 trait Store for Module<T: Config> as Nft {273274 275 276 CreatedCollectionCount: u32;277 278 ChainVersion: u64;279 280 281 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;282 283284 285 286 287 DestroyedCollectionCount: u32;288 289 290 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;291 292293 294 295 296 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;297 298 299 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;300 301 302 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;303 304305 306 307 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;308309 310 311 312 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;313314 315 316 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;317 318 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;319 320 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;321 322323 324 325 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;326 327328 329 330 331 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;332 333 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;334 335 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;336 337 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;338 339340 341 342 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;343 }344 add_extra_genesis {345 build(|config: &GenesisConfig<T>| {346 347 for (_num, _c) in &config.collection_id {348 <Module<T>>::init_collection(_c);349 }350351 for (_num, _c, _i) in &config.nft_item_id {352 <Module<T>>::init_nft_token(*_c, _i);353 }354355 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {356 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);357 }358359 for (_num, _c, _i) in &config.refungible_item_id {360 <Module<T>>::init_refungible_token(*_c, _i);361 }362 })363 }364}365366decl_event!(367 pub enum Event<T>368 where369 AccountId = <T as frame_system::Config>::AccountId,370 CrossAccountId = <T as Config>::CrossAccountId,371 {372 373 374 375 376 377 378 379 380 381 CollectionCreated(CollectionId, u8, AccountId),382383 384 385 386 387 388 389 390 391 392 ItemCreated(CollectionId, TokenId, CrossAccountId),393394 395 396 397 398 399 400 401 ItemDestroyed(CollectionId, TokenId),402403 404 405 406 407 408 409 410 411 412 413 414 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),415416 417 418 419 420 421 422 423 424 425 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),426 }427);428429decl_module! {430 pub struct Module<T: Config> for enum Call431 where432 origin: T::Origin433 {434 fn deposit_event() = default;435 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;436 type Error = Error<T>;437438 fn on_initialize(_now: T::BlockNumber) -> Weight {439 0440 }441442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 #[weight = <T as Config>::WeightInfo::create_collection()]459 #[transactional]460 pub fn create_collection(origin,461 collection_name: Vec<u16>,462 collection_description: Vec<u16>,463 token_prefix: Vec<u8>,464 mode: CollectionMode) -> DispatchResult {465466 467 let who = ensure_signed(origin)?;468469 470 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();471 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(472 &T::TreasuryAccountId::get(),473 T::CollectionCreationPrice::get(),474 ));475 <T as Config>::Currency::settle(476 &who,477 imbalance,478 WithdrawReasons::TRANSFER,479 ExistenceRequirement::KeepAlive,480 ).map_err(|_| Error::<T>::NoPermission)?;481482 let decimal_points = match mode {483 CollectionMode::Fungible(points) => points,484 _ => 0485 };486487 let created_count = CreatedCollectionCount::get();488 let destroyed_count = DestroyedCollectionCount::get();489490 491 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);492493 494 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);495 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);496 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);497 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);498499 500 let next_id = created_count501 .checked_add(1)502 .ok_or(Error::<T>::NumOverflow)?;503504 CreatedCollectionCount::put(next_id);505506 let limits = CollectionLimits {507 sponsored_data_size: CUSTOM_DATA_LIMIT,508 ..Default::default()509 };510511 512 let new_collection = Collection {513 owner: who.clone(),514 name: collection_name,515 mode: mode.clone(),516 mint_mode: false,517 access: AccessMode::Normal,518 description: collection_description,519 decimal_points,520 token_prefix,521 offchain_schema: Vec::new(),522 schema_version: SchemaVersion::ImageURL,523 sponsorship: SponsorshipState::Disabled,524 variable_on_chain_schema: Vec::new(),525 const_on_chain_schema: Vec::new(),526 limits,527 transfers_enabled: true,528 };529530 531 <CollectionById<T>>::insert(next_id, new_collection);532533 534 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));535536 Ok(())537 }538539 540 541 542 543 544 545 546 547 548 #[weight = <T as Config>::WeightInfo::destroy_collection()]549 #[transactional]550 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {551552 let sender = ensure_signed(origin)?;553 let collection = Self::get_collection(collection_id)?;554 Self::check_owner_permissions(&collection, &sender)?;555 if !collection.limits.owner_can_destroy {556 fail!(Error::<T>::NoPermission);557 }558559 <AddressTokens<T>>::remove_prefix(collection_id, None);560 <Allowances<T>>::remove_prefix(collection_id, None);561 <Balance<T>>::remove_prefix(collection_id, None);562 <ItemListIndex>::remove(collection_id);563 <AdminList<T>>::remove(collection_id);564 <CollectionById<T>>::remove(collection_id);565 <WhiteList<T>>::remove_prefix(collection_id, None);566567 <NftItemList<T>>::remove_prefix(collection_id, None);568 <FungibleItemList<T>>::remove_prefix(collection_id, None);569 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);570571 <NftTransferBasket<T>>::remove_prefix(collection_id, None);572 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);573 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);574575 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);576577 DestroyedCollectionCount::put(DestroyedCollectionCount::get()578 .checked_add(1)579 .ok_or(Error::<T>::NumOverflow)?);580581 Ok(())582 }583584 585 586 587 588 589 590 591 592 593 594 595 596 #[weight = <T as Config>::WeightInfo::add_to_white_list()]597 #[transactional]598 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{599600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let collection = Self::get_collection(collection_id)?;602603 Self::toggle_white_list_internal(604 &sender,605 &collection,606 &address,607 true,608 )?;609610 Ok(())611 }612613 614 615 616 617 618 619 620 621 622 623 624 625 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]626 #[transactional]627 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{628629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630 let collection = Self::get_collection(collection_id)?;631632 Self::toggle_white_list_internal(633 &sender,634 &collection,635 &address,636 false,637 )?;638639 Ok(())640 }641642 643 644 645 646 647 648 649 650 651 652 653 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]654 #[transactional]655 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult656 {657 let sender = ensure_signed(origin)?;658659 let mut target_collection = Self::get_collection(collection_id)?;660 Self::check_owner_permissions(&target_collection, &sender)?;661 target_collection.access = mode;662 target_collection.save()663 }664665 666 667 668 669 670 671 672 673 674 675 676 677 678 #[weight = <T as Config>::WeightInfo::set_mint_permission()]679 #[transactional]680 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult681 {682 let sender = ensure_signed(origin)?;683684 let mut target_collection = Self::get_collection(collection_id)?;685 Self::check_owner_permissions(&target_collection, &sender)?;686 target_collection.mint_mode = mint_permission;687 target_collection.save()688 }689690 691 692 693 694 695 696 697 698 699 700 701 #[weight = <T as Config>::WeightInfo::change_collection_owner()]702 #[transactional]703 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {704705 let sender = ensure_signed(origin)?;706 let mut target_collection = Self::get_collection(collection_id)?;707 Self::check_owner_permissions(&target_collection, &sender)?;708 target_collection.owner = new_owner;709 target_collection.save()710 }711712 713 714 715 716 717 718 719 720 721 722 723 724 725 #[weight = <T as Config>::WeightInfo::add_collection_admin()]726 #[transactional]727 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {728 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729 let collection = Self::get_collection(collection_id)?;730 Self::check_owner_or_admin_permissions(&collection, &sender)?;731 let mut admin_arr = <AdminList<T>>::get(collection_id);732733 match admin_arr.binary_search(&new_admin_id) {734 Ok(_) => {},735 Err(idx) => {736 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);737 admin_arr.insert(idx, new_admin_id);738 <AdminList<T>>::insert(collection_id, admin_arr);739 }740 }741 Ok(())742 }743744 745 746 747 748 749 750 751 752 753 754 755 756 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]757 #[transactional]758 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {759 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);760 let collection = Self::get_collection(collection_id)?;761 Self::check_owner_or_admin_permissions(&collection, &sender)?;762 let mut admin_arr = <AdminList<T>>::get(collection_id);763764 if let Ok(idx) = admin_arr.binary_search(&account_id) {765 admin_arr.remove(idx);766 <AdminList<T>>::insert(collection_id, admin_arr);767 }768 Ok(())769 }770771 772 773 774 775 776 777 778 779 780 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]781 #[transactional]782 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {783 let sender = ensure_signed(origin)?;784 let mut target_collection = Self::get_collection(collection_id)?;785 Self::check_owner_permissions(&target_collection, &sender)?;786787 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);788 target_collection.save()789 }790791 792 793 794 795 796 797 798 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]799 #[transactional]800 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {801 let sender = ensure_signed(origin)?;802803 let mut target_collection = Self::get_collection(collection_id)?;804 ensure!(805 target_collection.sponsorship.pending_sponsor() == Some(&sender),806 Error::<T>::ConfirmUnsetSponsorFail807 );808809 target_collection.sponsorship = SponsorshipState::Confirmed(sender);810 target_collection.save()811 }812813 814 815 816 817 818 819 820 821 822 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]823 #[transactional]824 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {825 let sender = ensure_signed(origin)?;826827 let mut target_collection = Self::get_collection(collection_id)?;828 Self::check_owner_permissions(&target_collection, &sender)?;829830 target_collection.sponsorship = SponsorshipState::Disabled;831 target_collection.save()832 }833834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857858 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]859 #[transactional]860 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {861 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);862 let collection = Self::get_collection(collection_id)?;863864 Self::create_item_internal(&sender, &collection, &owner, data)?;865866 collection.submit_logs()867 }868869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()888 .map(|data| { data.data_size() })889 .sum())]890 #[transactional]891 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {892893 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);894 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895 let collection = Self::get_collection(collection_id)?;896897 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;898899 collection.submit_logs()900 }901902 903904 905 906 907 908 909 910 911 912 913 914 915 #[weight = <T as Config>::WeightInfo::burn_item()]916 #[transactional]917 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {918919 let sender = ensure_signed(origin)?;920 let mut target_collection = Self::get_collection(collection_id)?;921922 Self::check_owner_permissions(&target_collection, &sender)?;923924 target_collection.transfers_enabled = value;925 target_collection.save()926 }927928 929 930 931 932 933 934 935 936 937 938 939 940 941 #[weight = <T as Config>::WeightInfo::burn_item()]942 #[transactional]943 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {944945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let target_collection = Self::get_collection(collection_id)?;947948 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;949950 target_collection.submit_logs()951 }952953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 #[weight = <T as Config>::WeightInfo::transfer()]977 #[transactional]978 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {979 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);980 let collection = Self::get_collection(collection_id)?;981982 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;983984 collection.submit_logs()985 }986987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 #[weight = <T as Config>::WeightInfo::approve()]1003 #[transactional]1004 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1005 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006 let collection = Self::get_collection(collection_id)?;10071008 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10091010 collection.submit_logs()1011 }10121013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 #[weight = <T as Config>::WeightInfo::transfer_from()]1033 #[transactional]1034 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1035 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036 let collection = Self::get_collection(collection_id)?;10371038 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10391040 collection.submit_logs()1041 }1042 1043 1044 1045 1046 10471048 10491050 10511052 1053 10541055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1068 #[transactional]1069 pub fn set_variable_meta_data (1070 origin,1071 collection_id: CollectionId,1072 item_id: TokenId,1073 data: Vec<u8>1074 ) -> DispatchResult {1075 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10761077 let collection = Self::get_collection(collection_id)?;10781079 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10801081 Ok(())1082 }10831084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 #[weight = <T as Config>::WeightInfo::set_schema_version()]1099 #[transactional]1100 pub fn set_schema_version(1101 origin,1102 collection_id: CollectionId,1103 version: SchemaVersion1104 ) -> DispatchResult {1105 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1106 let mut target_collection = Self::get_collection(collection_id)?;1107 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1108 target_collection.schema_version = version;1109 target_collection.save()1110 }11111112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1125 #[transactional]1126 pub fn set_offchain_schema(1127 origin,1128 collection_id: CollectionId,1129 schema: Vec<u8>1130 ) -> DispatchResult {1131 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1132 let mut target_collection = Self::get_collection(collection_id)?;1133 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11341135 1136 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11371138 target_collection.offchain_schema = schema;1139 target_collection.save()1140 }11411142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1155 #[transactional]1156 pub fn set_const_on_chain_schema (1157 origin,1158 collection_id: CollectionId,1159 schema: Vec<u8>1160 ) -> DispatchResult {1161 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1162 let mut target_collection = Self::get_collection(collection_id)?;1163 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11641165 1166 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11671168 target_collection.const_on_chain_schema = schema;1169 target_collection.save()1170 }11711172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1185 #[transactional]1186 pub fn set_variable_on_chain_schema (1187 origin,1188 collection_id: CollectionId,1189 schema: Vec<u8>1190 ) -> DispatchResult {1191 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1192 let mut target_collection = Self::get_collection(collection_id)?;1193 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11941195 1196 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11971198 target_collection.variable_on_chain_schema = schema;1199 target_collection.save()1200 }12011202 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1203 #[transactional]1204 pub fn set_collection_limits(1205 origin,1206 collection_id: u32,1207 new_limits: CollectionLimits<T::BlockNumber>,1208 ) -> DispatchResult {1209 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210 let mut target_collection = Self::get_collection(collection_id)?;1211 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1212 let old_limits = &target_collection.limits;12131214 1215 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1216 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1217 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1218 Error::<T>::CollectionLimitBoundsExceeded);12191220 1221 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1222 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12231224 ensure!(1225 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1226 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1227 Error::<T>::OwnerPermissionsCantBeReverted,1228 );12291230 target_collection.limits = new_limits;12311232 target_collection.save()1233 }1234 }1235}12361237impl<T: Config> Module<T> {1238 pub fn create_item_internal(1239 sender: &T::CrossAccountId,1240 collection: &CollectionHandle<T>,1241 owner: &T::CrossAccountId,1242 data: CreateItemData,1243 ) -> DispatchResult {1244 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1245 Self::validate_create_item_args(collection, &data)?;1246 Self::create_item_no_validation(collection, owner, data)?;12471248 Ok(())1249 }12501251 pub fn transfer_internal(1252 sender: &T::CrossAccountId,1253 recipient: &T::CrossAccountId,1254 target_collection: &CollectionHandle<T>,1255 item_id: TokenId,1256 value: u128,1257 ) -> DispatchResult {1258 target_collection.consume_gas(2000000)?;1259 1260 Self::is_correct_transfer(target_collection, recipient)?;12611262 1263 ensure!(1264 Self::is_item_owner(sender, target_collection, item_id)1265 || Self::is_owner_or_admin_permissions(target_collection, sender),1266 Error::<T>::NoPermission1267 );12681269 if target_collection.access == AccessMode::WhiteList {1270 Self::check_white_list(target_collection, sender)?;1271 Self::check_white_list(target_collection, recipient)?;1272 }12731274 match target_collection.mode {1275 CollectionMode::NFT => Self::transfer_nft(1276 target_collection,1277 item_id,1278 sender.clone(),1279 recipient.clone(),1280 )?,1281 CollectionMode::Fungible(_) => {1282 Self::transfer_fungible(target_collection, value, sender, recipient)?1283 }1284 CollectionMode::ReFungible => Self::transfer_refungible(1285 target_collection,1286 item_id,1287 value,1288 sender.clone(),1289 recipient.clone(),1290 )?,1291 _ => (),1292 };12931294 Self::deposit_event(RawEvent::Transfer(1295 target_collection.id,1296 item_id,1297 sender.clone(),1298 recipient.clone(),1299 value,1300 ));13011302 Ok(())1303 }13041305 pub fn approve_internal(1306 sender: &T::CrossAccountId,1307 spender: &T::CrossAccountId,1308 collection: &CollectionHandle<T>,1309 item_id: TokenId,1310 amount: u128,1311 ) -> DispatchResult {1312 collection.consume_gas(2000000)?;1313 Self::token_exists(collection, item_id)?;13141315 1316 let bypasses_limits = collection.limits.owner_can_transfer1317 && Self::is_owner_or_admin_permissions(collection, sender);13181319 let allowance_limit = if bypasses_limits {1320 None1321 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1322 Some(amount)1323 } else {1324 fail!(Error::<T>::NoPermission);1325 };13261327 if collection.access == AccessMode::WhiteList {1328 Self::check_white_list(collection, sender)?;1329 Self::check_white_list(collection, spender)?;1330 }13311332 let allowance: u128 = amount1333 .checked_add(<Allowances<T>>::get(1334 collection.id,1335 (item_id, sender.as_sub(), spender.as_sub()),1336 ))1337 .ok_or(Error::<T>::NumOverflow)?;1338 if let Some(limit) = allowance_limit {1339 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1340 }1341 <Allowances<T>>::insert(1342 collection.id,1343 (item_id, sender.as_sub(), spender.as_sub()),1344 allowance,1345 );13461347 if matches!(collection.mode, CollectionMode::NFT) {1348 1349 collection.log(ERC721Events::Approval {1350 owner: *sender.as_eth(),1351 approved: *spender.as_eth(),1352 token_id: item_id.into(),1353 })?;1354 }13551356 if matches!(collection.mode, CollectionMode::Fungible(_)) {1357 1358 collection.log(ERC20Events::Approval {1359 owner: *sender.as_eth(),1360 spender: *spender.as_eth(),1361 value: allowance.into(),1362 })?;1363 }13641365 Self::deposit_event(RawEvent::Approved(1366 collection.id,1367 item_id,1368 sender.clone(),1369 spender.clone(),1370 allowance,1371 ));1372 Ok(())1373 }13741375 pub fn transfer_from_internal(1376 sender: &T::CrossAccountId,1377 from: &T::CrossAccountId,1378 recipient: &T::CrossAccountId,1379 collection: &CollectionHandle<T>,1380 item_id: TokenId,1381 amount: u128,1382 ) -> DispatchResult {1383 collection.consume_gas(2000000)?;1384 1385 let approval: u128 =1386 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13871388 1389 Self::is_correct_transfer(collection, recipient)?;13901391 1392 ensure!(1393 approval >= amount1394 || (collection.limits.owner_can_transfer1395 && Self::is_owner_or_admin_permissions(collection, sender)),1396 Error::<T>::NoPermission1397 );13981399 if collection.access == AccessMode::WhiteList {1400 Self::check_white_list(collection, sender)?;1401 Self::check_white_list(collection, recipient)?;1402 }14031404 1405 let allowance = approval.saturating_sub(amount);1406 if allowance > 0 {1407 <Allowances<T>>::insert(1408 collection.id,1409 (item_id, from.as_sub(), sender.as_sub()),1410 allowance,1411 );1412 } else {1413 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1414 }14151416 match collection.mode {1417 CollectionMode::NFT => {1418 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1419 }1420 CollectionMode::Fungible(_) => {1421 Self::transfer_fungible(collection, amount, from, recipient)?1422 }1423 CollectionMode::ReFungible => Self::transfer_refungible(1424 collection,1425 item_id,1426 amount,1427 from.clone(),1428 recipient.clone(),1429 )?,1430 _ => (),1431 };14321433 if matches!(collection.mode, CollectionMode::Fungible(_)) {1434 collection.log(ERC20Events::Approval {1435 owner: *from.as_eth(),1436 spender: *sender.as_eth(),1437 value: allowance.into(),1438 })?;1439 }14401441 Ok(())1442 }14431444 pub fn set_variable_meta_data_internal(1445 sender: &T::CrossAccountId,1446 collection: &CollectionHandle<T>,1447 item_id: TokenId,1448 data: Vec<u8>,1449 ) -> DispatchResult {1450 Self::token_exists(collection, item_id)?;14511452 ensure!(1453 CUSTOM_DATA_LIMIT >= data.len() as u32,1454 Error::<T>::TokenVariableDataLimitExceeded1455 );14561457 1458 ensure!(1459 Self::is_item_owner(sender, collection, item_id)1460 || Self::is_owner_or_admin_permissions(collection, sender),1461 Error::<T>::NoPermission1462 );14631464 match collection.mode {1465 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1466 CollectionMode::ReFungible => {1467 Self::set_re_fungible_variable_data(collection, item_id, data)?1468 }1469 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1470 _ => fail!(Error::<T>::UnexpectedCollectionType),1471 };14721473 Ok(())1474 }14751476 pub fn create_multiple_items_internal(1477 sender: &T::CrossAccountId,1478 collection: &CollectionHandle<T>,1479 owner: &T::CrossAccountId,1480 items_data: Vec<CreateItemData>,1481 ) -> DispatchResult {1482 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14831484 for data in &items_data {1485 Self::validate_create_item_args(collection, data)?;1486 }1487 for data in &items_data {1488 Self::create_item_no_validation(collection, owner, data.clone())?;1489 }14901491 Ok(())1492 }14931494 pub fn burn_item_internal(1495 sender: &T::CrossAccountId,1496 collection: &CollectionHandle<T>,1497 item_id: TokenId,1498 value: u128,1499 ) -> DispatchResult {1500 ensure!(1501 Self::is_item_owner(sender, collection, item_id)1502 || (collection.limits.owner_can_transfer1503 && Self::is_owner_or_admin_permissions(collection, sender)),1504 Error::<T>::NoPermission1505 );15061507 if collection.access == AccessMode::WhiteList {1508 Self::check_white_list(collection, sender)?;1509 }15101511 match collection.mode {1512 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1513 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1514 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1515 _ => (),1516 };15171518 Ok(())1519 }15201521 pub fn toggle_white_list_internal(1522 sender: &T::CrossAccountId,1523 collection: &CollectionHandle<T>,1524 address: &T::CrossAccountId,1525 whitelisted: bool,1526 ) -> DispatchResult {1527 Self::check_owner_or_admin_permissions(collection, sender)?;15281529 if whitelisted {1530 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1531 } else {1532 <WhiteList<T>>::remove(collection.id, address.as_sub());1533 }15341535 Ok(())1536 }15371538 fn is_correct_transfer(1539 collection: &CollectionHandle<T>,1540 recipient: &T::CrossAccountId,1541 ) -> DispatchResult {1542 let collection_id = collection.id;15431544 1545 let account_items: u32 =1546 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1547 ensure!(1548 collection.limits.account_token_ownership_limit > account_items,1549 Error::<T>::AccountTokenLimitExceeded1550 );15511552 1553 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15541555 Ok(())1556 }15571558 fn can_create_items_in_collection(1559 collection: &CollectionHandle<T>,1560 sender: &T::CrossAccountId,1561 owner: &T::CrossAccountId,1562 amount: u32,1563 ) -> DispatchResult {1564 let collection_id = collection.id;15651566 1567 let total_items: u32 = ItemListIndex::get(collection_id)1568 .checked_add(amount)1569 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1570 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1571 as u32)1572 .checked_add(amount)1573 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1574 ensure!(1575 collection.limits.token_limit >= total_items,1576 Error::<T>::CollectionTokenLimitExceeded1577 );1578 ensure!(1579 collection.limits.account_token_ownership_limit >= account_items,1580 Error::<T>::AccountTokenLimitExceeded1581 );15821583 if !Self::is_owner_or_admin_permissions(collection, sender) {1584 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1585 Self::check_white_list(collection, owner)?;1586 Self::check_white_list(collection, sender)?;1587 }15881589 Ok(())1590 }15911592 fn validate_create_item_args(1593 target_collection: &CollectionHandle<T>,1594 data: &CreateItemData,1595 ) -> DispatchResult {1596 match target_collection.mode {1597 CollectionMode::NFT => {1598 if !matches!(data, CreateItemData::NFT(_)) {1599 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1600 }1601 }1602 CollectionMode::Fungible(_) => {1603 if !matches!(data, CreateItemData::Fungible(_)) {1604 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1605 }1606 }1607 CollectionMode::ReFungible => {1608 if let CreateItemData::ReFungible(data) = data {1609 1610 ensure!(1611 data.pieces <= MAX_REFUNGIBLE_PIECES,1612 Error::<T>::WrongRefungiblePieces1613 );1614 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1615 } else {1616 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1617 }1618 }1619 _ => {1620 fail!(Error::<T>::UnexpectedCollectionType);1621 }1622 };16231624 Ok(())1625 }16261627 fn create_item_no_validation(1628 collection: &CollectionHandle<T>,1629 owner: &T::CrossAccountId,1630 data: CreateItemData,1631 ) -> DispatchResult {1632 match data {1633 CreateItemData::NFT(data) => {1634 let item = NftItemType {1635 owner: owner.clone(),1636 const_data: data.const_data.into_inner(),1637 variable_data: data.variable_data.into_inner(),1638 };16391640 Self::add_nft_item(collection, item)?;1641 }1642 CreateItemData::Fungible(data) => {1643 Self::add_fungible_item(collection, owner, data.value)?;1644 }1645 CreateItemData::ReFungible(data) => {1646 let owner_list = vec![Ownership {1647 owner: owner.clone(),1648 fraction: data.pieces,1649 }];16501651 let item = ReFungibleItemType {1652 owner: owner_list,1653 const_data: data.const_data.into_inner(),1654 variable_data: data.variable_data.into_inner(),1655 };16561657 Self::add_refungible_item(collection, item)?;1658 }1659 };16601661 Ok(())1662 }16631664 fn add_fungible_item(1665 collection: &CollectionHandle<T>,1666 owner: &T::CrossAccountId,1667 value: u128,1668 ) -> DispatchResult {1669 let collection_id = collection.id;16701671 1672 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16731674 1675 let item = FungibleItemType {1676 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1677 };1678 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16791680 1681 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1682 .checked_add(value)1683 .ok_or(Error::<T>::NumOverflow)?;1684 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16851686 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1687 Ok(())1688 }16891690 fn add_refungible_item(1691 collection: &CollectionHandle<T>,1692 item: ReFungibleItemType<T::CrossAccountId>,1693 ) -> DispatchResult {1694 let collection_id = collection.id;16951696 let current_index = <ItemListIndex>::get(collection_id)1697 .checked_add(1)1698 .ok_or(Error::<T>::NumOverflow)?;1699 let itemcopy = item.clone();17001701 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1702 let item_owner = item.owner.first().expect("only one owner is defined");17031704 let value = item_owner.fraction;1705 let owner = item_owner.owner.clone();17061707 Self::add_token_index(collection_id, current_index, &owner)?;17081709 <ItemListIndex>::insert(collection_id, current_index);1710 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17111712 1713 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1714 .checked_add(value)1715 .ok_or(Error::<T>::NumOverflow)?;1716 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17171718 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1719 Ok(())1720 }17211722 fn add_nft_item(1723 collection: &CollectionHandle<T>,1724 item: NftItemType<T::CrossAccountId>,1725 ) -> DispatchResult {1726 let collection_id = collection.id;17271728 let current_index = <ItemListIndex>::get(collection_id)1729 .checked_add(1)1730 .ok_or(Error::<T>::NumOverflow)?;17311732 let item_owner = item.owner.clone();1733 Self::add_token_index(collection_id, current_index, &item.owner)?;17341735 <ItemListIndex>::insert(collection_id, current_index);1736 <NftItemList<T>>::insert(collection_id, current_index, item);17371738 1739 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1740 .checked_add(1)1741 .ok_or(Error::<T>::NumOverflow)?;1742 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17431744 collection.log(ERC721Events::Transfer {1745 from: H160::default(),1746 to: *item_owner.as_eth(),1747 token_id: current_index.into(),1748 })?;1749 Self::deposit_event(RawEvent::ItemCreated(1750 collection_id,1751 current_index,1752 item_owner,1753 ));1754 Ok(())1755 }17561757 fn burn_refungible_item(1758 collection: &CollectionHandle<T>,1759 item_id: TokenId,1760 owner: &T::CrossAccountId,1761 ) -> DispatchResult {1762 let collection_id = collection.id;17631764 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1765 .ok_or(Error::<T>::TokenNotFound)?;1766 let rft_balance = token1767 .owner1768 .iter()1769 .find(|&i| i.owner == *owner)1770 .ok_or(Error::<T>::TokenNotFound)?;1771 Self::remove_token_index(collection_id, item_id, owner)?;17721773 1774 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1775 .checked_sub(rft_balance.fraction)1776 .ok_or(Error::<T>::NumOverflow)?;1777 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17781779 1780 let index = token1781 .owner1782 .iter()1783 .position(|i| i.owner == *owner)1784 .expect("owned item is exists");1785 token.owner.remove(index);1786 let owner_count = token.owner.len();17871788 1789 if owner_count == 0 {1790 <ReFungibleItemList<T>>::remove(collection_id, item_id);1791 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1792 } else {1793 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1794 }17951796 Ok(())1797 }17981799 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1800 let collection_id = collection.id;18011802 let item =1803 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1804 Self::remove_token_index(collection_id, item_id, &item.owner)?;18051806 1807 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1808 .checked_sub(1)1809 .ok_or(Error::<T>::NumOverflow)?;1810 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1811 <NftItemList<T>>::remove(collection_id, item_id);1812 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18131814 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1815 Ok(())1816 }18171818 fn burn_fungible_item(1819 owner: &T::CrossAccountId,1820 collection: &CollectionHandle<T>,1821 value: u128,1822 ) -> DispatchResult {1823 let collection_id = collection.id;18241825 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1826 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18271828 1829 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830 .checked_sub(value)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834 if balance.value - value > 0 {1835 balance.value -= value;1836 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1837 } else {1838 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1839 }18401841 collection.log(ERC20Events::Transfer {1842 from: *owner.as_eth(),1843 to: H160::default(),1844 value: value.into(),1845 })?;1846 Ok(())1847 }18481849 pub fn get_collection(1850 collection_id: CollectionId,1851 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1852 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1853 }18541855 fn check_owner_permissions(1856 target_collection: &CollectionHandle<T>,1857 subject: &T::AccountId,1858 ) -> DispatchResult {1859 ensure!(1860 *subject == target_collection.owner,1861 Error::<T>::NoPermission1862 );18631864 Ok(())1865 }18661867 fn is_owner_or_admin_permissions(1868 collection: &CollectionHandle<T>,1869 subject: &T::CrossAccountId,1870 ) -> bool {1871 *subject.as_sub() == collection.owner1872 || <AdminList<T>>::get(collection.id).contains(subject)1873 }18741875 fn check_owner_or_admin_permissions(1876 collection: &CollectionHandle<T>,1877 subject: &T::CrossAccountId,1878 ) -> DispatchResult {1879 ensure!(1880 Self::is_owner_or_admin_permissions(collection, subject),1881 Error::<T>::NoPermission1882 );18831884 Ok(())1885 }18861887 fn owned_amount(1888 subject: &T::CrossAccountId,1889 target_collection: &CollectionHandle<T>,1890 item_id: TokenId,1891 ) -> Option<u128> {1892 let collection_id = target_collection.id;18931894 match target_collection.mode {1895 CollectionMode::NFT => {1896 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1897 }1898 CollectionMode::Fungible(_) => {1899 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1900 }1901 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1902 .owner1903 .iter()1904 .find(|i| i.owner == *subject)1905 .map(|i| i.fraction),1906 CollectionMode::Invalid => None,1907 }1908 }19091910 fn is_item_owner(1911 subject: &T::CrossAccountId,1912 target_collection: &CollectionHandle<T>,1913 item_id: TokenId,1914 ) -> bool {1915 match target_collection.mode {1916 CollectionMode::Fungible(_) => true,1917 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1918 }1919 }19201921 fn check_white_list(1922 collection: &CollectionHandle<T>,1923 address: &T::CrossAccountId,1924 ) -> DispatchResult {1925 let collection_id = collection.id;19261927 let mes = Error::<T>::AddresNotInWhiteList;1928 ensure!(1929 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1930 mes1931 );19321933 Ok(())1934 }19351936 1937 1938 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1939 let collection_id = target_collection.id;1940 let exists = match target_collection.mode {1941 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1942 CollectionMode::Fungible(_) => true,1943 CollectionMode::ReFungible => {1944 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1945 }1946 _ => false,1947 };19481949 ensure!(exists, Error::<T>::TokenNotFound);1950 Ok(())1951 }19521953 fn transfer_fungible(1954 collection: &CollectionHandle<T>,1955 value: u128,1956 owner: &T::CrossAccountId,1957 recipient: &T::CrossAccountId,1958 ) -> DispatchResult {1959 let collection_id = collection.id;19601961 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1962 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19631964 1965 Self::add_fungible_item(collection, recipient, value)?;19661967 1968 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19691970 1971 if balance.value == value {1972 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1973 } else {1974 balance.value -= value;1975 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1976 }19771978 collection.log(ERC20Events::Transfer {1979 from: *owner.as_eth(),1980 to: *recipient.as_eth(),1981 value: value.into(),1982 })?;1983 Self::deposit_event(RawEvent::Transfer(1984 collection.id,1985 1,1986 owner.clone(),1987 recipient.clone(),1988 value,1989 ));19901991 Ok(())1992 }19931994 fn transfer_refungible(1995 collection: &CollectionHandle<T>,1996 item_id: TokenId,1997 value: u128,1998 owner: T::CrossAccountId,1999 new_owner: T::CrossAccountId,2000 ) -> DispatchResult {2001 let collection_id = collection.id;2002 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2003 .ok_or(Error::<T>::TokenNotFound)?;20042005 let item = full_item2006 .owner2007 .iter()2008 .find(|i| i.owner == owner)2009 .ok_or(Error::<T>::TokenNotFound)?;2010 let amount = item.fraction;20112012 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20132014 2015 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2016 .checked_sub(value)2017 .ok_or(Error::<T>::NumOverflow)?;2018 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20192020 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2021 .checked_add(value)2022 .ok_or(Error::<T>::NumOverflow)?;2023 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20242025 let old_owner = item.owner.clone();2026 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20272028 let mut new_full_item = full_item.clone();2029 2030 if amount == value && !new_owner_has_account {2031 2032 2033 new_full_item2034 .owner2035 .iter_mut()2036 .find(|i| i.owner == owner)2037 .expect("old owner does present in refungible")2038 .owner = new_owner.clone();2039 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20402041 2042 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2043 } else {2044 new_full_item2045 .owner2046 .iter_mut()2047 .find(|i| i.owner == owner)2048 .expect("old owner does present in refungible")2049 .fraction -= value;20502051 2052 if new_owner_has_account {2053 2054 new_full_item2055 .owner2056 .iter_mut()2057 .find(|i| i.owner == new_owner)2058 .expect("new owner has account")2059 .fraction += value;2060 } else {2061 2062 new_full_item.owner.push(Ownership {2063 owner: new_owner.clone(),2064 fraction: value,2065 });2066 Self::add_token_index(collection_id, item_id, &new_owner)?;2067 }20682069 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2070 }20712072 Self::deposit_event(RawEvent::Transfer(2073 collection.id,2074 item_id,2075 owner,2076 new_owner,2077 amount,2078 ));20792080 Ok(())2081 }20822083 fn transfer_nft(2084 collection: &CollectionHandle<T>,2085 item_id: TokenId,2086 sender: T::CrossAccountId,2087 new_owner: T::CrossAccountId,2088 ) -> DispatchResult {2089 let collection_id = collection.id;2090 let mut item =2091 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20922093 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20942095 2096 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2097 .checked_sub(1)2098 .ok_or(Error::<T>::NumOverflow)?;2099 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21002101 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2102 .checked_add(1)2103 .ok_or(Error::<T>::NumOverflow)?;2104 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21052106 2107 let old_owner = item.owner.clone();2108 item.owner = new_owner.clone();2109 <NftItemList<T>>::insert(collection_id, item_id, item);21102111 2112 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21132114 collection.log(ERC721Events::Transfer {2115 from: *sender.as_eth(),2116 to: *new_owner.as_eth(),2117 token_id: item_id.into(),2118 })?;2119 Self::deposit_event(RawEvent::Transfer(2120 collection.id,2121 item_id,2122 sender,2123 new_owner,2124 1,2125 ));21262127 Ok(())2128 }21292130 fn set_re_fungible_variable_data(2131 collection: &CollectionHandle<T>,2132 item_id: TokenId,2133 data: Vec<u8>,2134 ) -> DispatchResult {2135 let collection_id = collection.id;2136 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2137 .ok_or(Error::<T>::TokenNotFound)?;21382139 item.variable_data = data;21402141 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21422143 Ok(())2144 }21452146 fn set_nft_variable_data(2147 collection: &CollectionHandle<T>,2148 item_id: TokenId,2149 data: Vec<u8>,2150 ) -> DispatchResult {2151 let collection_id = collection.id;2152 let mut item =2153 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21542155 item.variable_data = data;21562157 <NftItemList<T>>::insert(collection_id, item_id, item);21582159 Ok(())2160 }21612162 #[allow(dead_code)]2163 fn init_collection(item: &Collection<T>) {2164 2165 assert!(2166 item.decimal_points <= MAX_DECIMAL_POINTS,2167 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2168 );2169 assert!(2170 item.name.len() <= 64,2171 "Collection name can not be longer than 63 char"2172 );2173 assert!(2174 item.name.len() <= 256,2175 "Collection description can not be longer than 255 char"2176 );2177 assert!(2178 item.token_prefix.len() <= 16,2179 "Token prefix can not be longer than 15 char"2180 );21812182 2183 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21842185 CreatedCollectionCount::put(next_id);2186 }21872188 #[allow(dead_code)]2189 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2190 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21912192 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21932194 <ItemListIndex>::insert(collection_id, current_index);21952196 2197 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2198 .checked_add(1)2199 .unwrap();2200 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2201 }22022203 #[allow(dead_code)]2204 fn init_fungible_token(2205 collection_id: CollectionId,2206 owner: &T::CrossAccountId,2207 item: &FungibleItemType,2208 ) {2209 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22102211 Self::add_token_index(collection_id, current_index, owner).unwrap();22122213 <ItemListIndex>::insert(collection_id, current_index);22142215 2216 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2217 .checked_add(item.value)2218 .unwrap();2219 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2220 }22212222 #[allow(dead_code)]2223 fn init_refungible_token(2224 collection_id: CollectionId,2225 item: &ReFungibleItemType<T::CrossAccountId>,2226 ) {2227 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22282229 let value = item.owner.first().unwrap().fraction;2230 let owner = item.owner.first().unwrap().owner.clone();22312232 Self::add_token_index(collection_id, current_index, &owner).unwrap();22332234 <ItemListIndex>::insert(collection_id, current_index);22352236 2237 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2238 .checked_add(value)2239 .unwrap();2240 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241 }22422243 fn add_token_index(2244 collection_id: CollectionId,2245 item_index: TokenId,2246 owner: &T::CrossAccountId,2247 ) -> DispatchResult {2248 2249 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2250 2251 let count = <AccountItemCount<T>>::get(owner.as_sub());2252 ensure!(2253 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2254 Error::<T>::AddressOwnershipLimitExceeded2255 );22562257 <AccountItemCount<T>>::insert(2258 owner.as_sub(),2259 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2260 );2261 } else {2262 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2263 }22642265 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2266 if list_exists {2267 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2268 let item_contains = list.contains(&item_index.clone());22692270 if !item_contains {2271 list.push(item_index);2272 }22732274 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2275 } else {2276 let itm = vec![item_index];2277 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2278 }22792280 Ok(())2281 }22822283 fn remove_token_index(2284 collection_id: CollectionId,2285 item_index: TokenId,2286 owner: &T::CrossAccountId,2287 ) -> DispatchResult {2288 2289 <AccountItemCount<T>>::insert(2290 owner.as_sub(),2291 <AccountItemCount<T>>::get(owner.as_sub())2292 .checked_sub(1)2293 .ok_or(Error::<T>::NumOverflow)?,2294 );22952296 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2297 if list_exists {2298 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2299 let item_contains = list.contains(&item_index.clone());23002301 if item_contains {2302 list.retain(|&item| item != item_index);2303 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2304 }2305 }23062307 Ok(())2308 }23092310 fn move_token_index(2311 collection_id: CollectionId,2312 item_index: TokenId,2313 old_owner: &T::CrossAccountId,2314 new_owner: &T::CrossAccountId,2315 ) -> DispatchResult {2316 Self::remove_token_index(collection_id, item_index, old_owner)?;2317 Self::add_token_index(collection_id, item_index, new_owner)?;23182319 Ok(())2320 }2321}23222323sp_api::decl_runtime_apis! {2324 pub trait NftApi {2325 2326 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2327 }2328}