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_chain_limits() -> Weight;92 fn set_contract_sponsoring_rate_limit() -> Weight;93 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;94 fn toggle_contract_white_list() -> Weight;95 fn add_to_contract_white_list() -> Weight;96 fn remove_from_contract_white_list() -> Weight;97 fn set_collection_limits() -> Weight;98}99100decl_error! {101 102 pub enum Error for Module<T: Config> {103 104 TotalCollectionsLimitExceeded,105 106 CollectionDecimalPointLimitExceeded,107 108 CollectionNameLimitExceeded,109 110 CollectionDescriptionLimitExceeded,111 112 CollectionTokenPrefixLimitExceeded,113 114 CollectionNotFound,115 116 TokenNotFound,117 118 AdminNotFound,119 120 NumOverflow,121 122 AlreadyAdmin,123 124 NoPermission,125 126 ConfirmUnsetSponsorFail,127 128 PublicMintingNotAllowed,129 130 MustBeTokenOwner,131 132 TokenValueTooLow,133 134 NftSizeLimitExceeded,135 136 ApproveNotFound,137 138 TokenValueNotEnough,139 140 ApproveRequired,141 142 AddresNotInWhiteList,143 144 CollectionAdminsLimitExceeded,145 146 AddressOwnershipLimitExceeded,147 148 EmptyArgument,149 150 TokenConstDataLimitExceeded,151 152 TokenVariableDataLimitExceeded,153 154 NotNftDataUsedToMintNftCollectionToken,155 156 NotFungibleDataUsedToMintFungibleCollectionToken,157 158 NotReFungibleDataUsedToMintReFungibleCollectionToken,159 160 UnexpectedCollectionType,161 162 CantStoreMetadataInFungibleTokens,163 164 CollectionTokenLimitExceeded,165 166 AccountTokenLimitExceeded,167 168 CollectionLimitBoundsExceeded,169 170 OwnerPermissionsCantBeReverted,171 172 SchemaDataLimitExceeded,173 174 WrongRefungiblePieces,175 176 BadCreateRefungibleCall,177 178 OutOfGas,179 180 TransferNotAllowed,181 }182}183184#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]185pub struct CollectionHandle<T: Config> {186 pub id: CollectionId,187 collection: Collection<T>,188 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,189}190impl<T: Config> CollectionHandle<T> {191 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {192 <CollectionById<T>>::get(id).map(|collection| Self {193 id,194 collection,195 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(196 eth::collection_id_to_address(id),197 gas_limit,198 ),199 })200 }201 pub fn get(id: CollectionId) -> Option<Self> {202 Self::get_with_gas_limit(id, u64::MAX)203 }204 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {205 self.recorder.log_sub(log)206 }207 fn consume_gas(&self, gas: u64) -> DispatchResult {208 self.recorder.consume_gas_sub(gas)209 }210 pub fn submit_logs(self) -> DispatchResult {211 self.recorder.submit_logs()212 }213 pub fn save(self) -> DispatchResult {214 self.recorder.submit_logs()?;215 <CollectionById<T>>::insert(self.id, self.collection);216 Ok(())217 }218}219impl<T: Config> Deref for CollectionHandle<T> {220 type Target = Collection<T>;221222 fn deref(&self) -> &Self::Target {223 &self.collection224 }225}226227impl<T: Config> DerefMut for CollectionHandle<T> {228 fn deref_mut(&mut self) -> &mut Self::Target {229 &mut self.collection230 }231}232233pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {234 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;235236 237 type WeightInfo: WeightInfo;238239 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;240 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;241242 type CrossAccountId: CrossAccountId<Self::AccountId>;243 type Currency: Currency<Self::AccountId>;244 type CollectionCreationPrice: Get<245 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,246 >;247 type TreasuryAccountId: Get<Self::AccountId>;248}249250251252253254255256257258259260261262263264265266267268269270271272decl_storage! {273 trait Store for Module<T: Config> as Nft {274275 276 277 CreatedCollectionCount: u32;278 279 ChainVersion: u64;280 281 282 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;283 284285 286 287 288 DestroyedCollectionCount: u32;289 290 291 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;292 293294 295 296 297 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;298 299 300 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;301 302 303 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;304 305306 307 308 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;309310 311 312 313 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;314315 316 317 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;318 319 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;320 321 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;322 323324 325 326 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;327 328329 330 331 332 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;333 334 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;335 336 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;337 338 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;339 340341 342 343 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;344 }345 add_extra_genesis {346 build(|config: &GenesisConfig<T>| {347 348 for (_num, _c) in &config.collection_id {349 <Module<T>>::init_collection(_c);350 }351352 for (_num, _c, _i) in &config.nft_item_id {353 <Module<T>>::init_nft_token(*_c, _i);354 }355356 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {357 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);358 }359360 for (_num, _c, _i) in &config.refungible_item_id {361 <Module<T>>::init_refungible_token(*_c, _i);362 }363 })364 }365}366367decl_event!(368 pub enum Event<T>369 where370 AccountId = <T as frame_system::Config>::AccountId,371 CrossAccountId = <T as Config>::CrossAccountId,372 {373 374 375 376 377 378 379 380 381 382 CollectionCreated(CollectionId, u8, AccountId),383384 385 386 387 388 389 390 391 392 393 ItemCreated(CollectionId, TokenId, CrossAccountId),394395 396 397 398 399 400 401 402 ItemDestroyed(CollectionId, TokenId),403404 405 406 407 408 409 410 411 412 413 414 415 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),416417 418 419 420 421 422 423 424 425 426 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),427 }428);429430decl_module! {431 pub struct Module<T: Config> for enum Call432 where433 origin: T::Origin434 {435 fn deposit_event() = default;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 let CreateItemData::NFT(data) = data {1599 1600 ensure!(1601 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1602 Error::<T>::TokenConstDataLimitExceeded1603 );1604 ensure!(1605 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1606 Error::<T>::TokenVariableDataLimitExceeded1607 );1608 } else {1609 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1610 }1611 }1612 CollectionMode::Fungible(_) => {1613 if let CreateItemData::Fungible(_) = data {1614 } else {1615 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1616 }1617 }1618 CollectionMode::ReFungible => {1619 if let CreateItemData::ReFungible(data) = data {1620 1621 ensure!(1622 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1623 Error::<T>::TokenConstDataLimitExceeded1624 );1625 ensure!(1626 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1627 Error::<T>::TokenVariableDataLimitExceeded1628 );16291630 1631 ensure!(1632 data.pieces <= MAX_REFUNGIBLE_PIECES,1633 Error::<T>::WrongRefungiblePieces1634 );1635 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1636 } else {1637 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1638 }1639 }1640 _ => {1641 fail!(Error::<T>::UnexpectedCollectionType);1642 }1643 };16441645 Ok(())1646 }16471648 fn create_item_no_validation(1649 collection: &CollectionHandle<T>,1650 owner: &T::CrossAccountId,1651 data: CreateItemData,1652 ) -> DispatchResult {1653 match data {1654 CreateItemData::NFT(data) => {1655 let item = NftItemType {1656 owner: owner.clone(),1657 const_data: data.const_data.into_inner(),1658 variable_data: data.variable_data.into_inner(),1659 };16601661 Self::add_nft_item(collection, item)?;1662 }1663 CreateItemData::Fungible(data) => {1664 Self::add_fungible_item(collection, owner, data.value)?;1665 }1666 CreateItemData::ReFungible(data) => {1667 let owner_list = vec![Ownership {1668 owner: owner.clone(),1669 fraction: data.pieces,1670 }];16711672 let item = ReFungibleItemType {1673 owner: owner_list,1674 const_data: data.const_data.into_inner(),1675 variable_data: data.variable_data.into_inner(),1676 };16771678 Self::add_refungible_item(collection, item)?;1679 }1680 };16811682 Ok(())1683 }16841685 fn add_fungible_item(1686 collection: &CollectionHandle<T>,1687 owner: &T::CrossAccountId,1688 value: u128,1689 ) -> DispatchResult {1690 let collection_id = collection.id;16911692 1693 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16941695 1696 let item = FungibleItemType {1697 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1698 };1699 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17001701 1702 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1703 .checked_add(value)1704 .ok_or(Error::<T>::NumOverflow)?;1705 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17061707 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1708 Ok(())1709 }17101711 fn add_refungible_item(1712 collection: &CollectionHandle<T>,1713 item: ReFungibleItemType<T::CrossAccountId>,1714 ) -> DispatchResult {1715 let collection_id = collection.id;17161717 let current_index = <ItemListIndex>::get(collection_id)1718 .checked_add(1)1719 .ok_or(Error::<T>::NumOverflow)?;1720 let itemcopy = item.clone();17211722 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1723 let item_owner = item.owner.first().expect("only one owner is defined");17241725 let value = item_owner.fraction;1726 let owner = item_owner.owner.clone();17271728 Self::add_token_index(collection_id, current_index, &owner)?;17291730 <ItemListIndex>::insert(collection_id, current_index);1731 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17321733 1734 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1735 .checked_add(value)1736 .ok_or(Error::<T>::NumOverflow)?;1737 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17381739 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1740 Ok(())1741 }17421743 fn add_nft_item(1744 collection: &CollectionHandle<T>,1745 item: NftItemType<T::CrossAccountId>,1746 ) -> DispatchResult {1747 let collection_id = collection.id;17481749 let current_index = <ItemListIndex>::get(collection_id)1750 .checked_add(1)1751 .ok_or(Error::<T>::NumOverflow)?;17521753 let item_owner = item.owner.clone();1754 Self::add_token_index(collection_id, current_index, &item.owner)?;17551756 <ItemListIndex>::insert(collection_id, current_index);1757 <NftItemList<T>>::insert(collection_id, current_index, item);17581759 1760 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1761 .checked_add(1)1762 .ok_or(Error::<T>::NumOverflow)?;1763 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17641765 collection.log(ERC721Events::Transfer {1766 from: H160::default(),1767 to: *item_owner.as_eth(),1768 token_id: current_index.into(),1769 })?;1770 Self::deposit_event(RawEvent::ItemCreated(1771 collection_id,1772 current_index,1773 item_owner,1774 ));1775 Ok(())1776 }17771778 fn burn_refungible_item(1779 collection: &CollectionHandle<T>,1780 item_id: TokenId,1781 owner: &T::CrossAccountId,1782 ) -> DispatchResult {1783 let collection_id = collection.id;17841785 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1786 .ok_or(Error::<T>::TokenNotFound)?;1787 let rft_balance = token1788 .owner1789 .iter()1790 .find(|&i| i.owner == *owner)1791 .ok_or(Error::<T>::TokenNotFound)?;1792 Self::remove_token_index(collection_id, item_id, owner)?;17931794 1795 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1796 .checked_sub(rft_balance.fraction)1797 .ok_or(Error::<T>::NumOverflow)?;1798 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17991800 1801 let index = token1802 .owner1803 .iter()1804 .position(|i| i.owner == *owner)1805 .expect("owned item is exists");1806 token.owner.remove(index);1807 let owner_count = token.owner.len();18081809 1810 if owner_count == 0 {1811 <ReFungibleItemList<T>>::remove(collection_id, item_id);1812 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1813 } else {1814 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1815 }18161817 Ok(())1818 }18191820 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1821 let collection_id = collection.id;18221823 let item =1824 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1825 Self::remove_token_index(collection_id, item_id, &item.owner)?;18261827 1828 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1829 .checked_sub(1)1830 .ok_or(Error::<T>::NumOverflow)?;1831 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1832 <NftItemList<T>>::remove(collection_id, item_id);1833 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18341835 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1836 Ok(())1837 }18381839 fn burn_fungible_item(1840 owner: &T::CrossAccountId,1841 collection: &CollectionHandle<T>,1842 value: u128,1843 ) -> DispatchResult {1844 let collection_id = collection.id;18451846 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1847 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18481849 1850 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1851 .checked_sub(value)1852 .ok_or(Error::<T>::NumOverflow)?;1853 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18541855 if balance.value - value > 0 {1856 balance.value -= value;1857 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1858 } else {1859 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1860 }18611862 collection.log(ERC20Events::Transfer {1863 from: *owner.as_eth(),1864 to: H160::default(),1865 value: value.into(),1866 })?;1867 Ok(())1868 }18691870 pub fn get_collection(1871 collection_id: CollectionId,1872 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1873 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1874 }18751876 fn check_owner_permissions(1877 target_collection: &CollectionHandle<T>,1878 subject: &T::AccountId,1879 ) -> DispatchResult {1880 ensure!(1881 *subject == target_collection.owner,1882 Error::<T>::NoPermission1883 );18841885 Ok(())1886 }18871888 fn is_owner_or_admin_permissions(1889 collection: &CollectionHandle<T>,1890 subject: &T::CrossAccountId,1891 ) -> bool {1892 *subject.as_sub() == collection.owner1893 || <AdminList<T>>::get(collection.id).contains(subject)1894 }18951896 fn check_owner_or_admin_permissions(1897 collection: &CollectionHandle<T>,1898 subject: &T::CrossAccountId,1899 ) -> DispatchResult {1900 ensure!(1901 Self::is_owner_or_admin_permissions(collection, subject),1902 Error::<T>::NoPermission1903 );19041905 Ok(())1906 }19071908 fn owned_amount(1909 subject: &T::CrossAccountId,1910 target_collection: &CollectionHandle<T>,1911 item_id: TokenId,1912 ) -> Option<u128> {1913 let collection_id = target_collection.id;19141915 match target_collection.mode {1916 CollectionMode::NFT => {1917 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1918 }1919 CollectionMode::Fungible(_) => {1920 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1921 }1922 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1923 .owner1924 .iter()1925 .find(|i| i.owner == *subject)1926 .map(|i| i.fraction),1927 CollectionMode::Invalid => None,1928 }1929 }19301931 fn is_item_owner(1932 subject: &T::CrossAccountId,1933 target_collection: &CollectionHandle<T>,1934 item_id: TokenId,1935 ) -> bool {1936 match target_collection.mode {1937 CollectionMode::Fungible(_) => true,1938 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1939 }1940 }19411942 fn check_white_list(1943 collection: &CollectionHandle<T>,1944 address: &T::CrossAccountId,1945 ) -> DispatchResult {1946 let collection_id = collection.id;19471948 let mes = Error::<T>::AddresNotInWhiteList;1949 ensure!(1950 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1951 mes1952 );19531954 Ok(())1955 }19561957 1958 1959 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1960 let collection_id = target_collection.id;1961 let exists = match target_collection.mode {1962 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1963 CollectionMode::Fungible(_) => true,1964 CollectionMode::ReFungible => {1965 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1966 }1967 _ => false,1968 };19691970 ensure!(exists, Error::<T>::TokenNotFound);1971 Ok(())1972 }19731974 fn transfer_fungible(1975 collection: &CollectionHandle<T>,1976 value: u128,1977 owner: &T::CrossAccountId,1978 recipient: &T::CrossAccountId,1979 ) -> DispatchResult {1980 let collection_id = collection.id;19811982 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1983 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19841985 1986 Self::add_fungible_item(collection, recipient, value)?;19871988 1989 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19901991 1992 if balance.value == value {1993 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1994 } else {1995 balance.value -= value;1996 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1997 }19981999 collection.log(ERC20Events::Transfer {2000 from: *owner.as_eth(),2001 to: *recipient.as_eth(),2002 value: value.into(),2003 })?;2004 Self::deposit_event(RawEvent::Transfer(2005 collection.id,2006 1,2007 owner.clone(),2008 recipient.clone(),2009 value,2010 ));20112012 Ok(())2013 }20142015 fn transfer_refungible(2016 collection: &CollectionHandle<T>,2017 item_id: TokenId,2018 value: u128,2019 owner: T::CrossAccountId,2020 new_owner: T::CrossAccountId,2021 ) -> DispatchResult {2022 let collection_id = collection.id;2023 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2024 .ok_or(Error::<T>::TokenNotFound)?;20252026 let item = full_item2027 .owner2028 .iter()2029 .find(|i| i.owner == owner)2030 .ok_or(Error::<T>::TokenNotFound)?;2031 let amount = item.fraction;20322033 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20342035 2036 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2037 .checked_sub(value)2038 .ok_or(Error::<T>::NumOverflow)?;2039 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20402041 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2042 .checked_add(value)2043 .ok_or(Error::<T>::NumOverflow)?;2044 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20452046 let old_owner = item.owner.clone();2047 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20482049 let mut new_full_item = full_item.clone();2050 2051 if amount == value && !new_owner_has_account {2052 2053 2054 new_full_item2055 .owner2056 .iter_mut()2057 .find(|i| i.owner == owner)2058 .expect("old owner does present in refungible")2059 .owner = new_owner.clone();2060 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20612062 2063 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2064 } else {2065 new_full_item2066 .owner2067 .iter_mut()2068 .find(|i| i.owner == owner)2069 .expect("old owner does present in refungible")2070 .fraction -= value;20712072 2073 if new_owner_has_account {2074 2075 new_full_item2076 .owner2077 .iter_mut()2078 .find(|i| i.owner == new_owner)2079 .expect("new owner has account")2080 .fraction += value;2081 } else {2082 2083 new_full_item.owner.push(Ownership {2084 owner: new_owner.clone(),2085 fraction: value,2086 });2087 Self::add_token_index(collection_id, item_id, &new_owner)?;2088 }20892090 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2091 }20922093 Self::deposit_event(RawEvent::Transfer(2094 collection.id,2095 item_id,2096 owner,2097 new_owner,2098 amount,2099 ));21002101 Ok(())2102 }21032104 fn transfer_nft(2105 collection: &CollectionHandle<T>,2106 item_id: TokenId,2107 sender: T::CrossAccountId,2108 new_owner: T::CrossAccountId,2109 ) -> DispatchResult {2110 let collection_id = collection.id;2111 let mut item =2112 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21132114 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21152116 2117 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2118 .checked_sub(1)2119 .ok_or(Error::<T>::NumOverflow)?;2120 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21212122 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2123 .checked_add(1)2124 .ok_or(Error::<T>::NumOverflow)?;2125 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21262127 2128 let old_owner = item.owner.clone();2129 item.owner = new_owner.clone();2130 <NftItemList<T>>::insert(collection_id, item_id, item);21312132 2133 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21342135 collection.log(ERC721Events::Transfer {2136 from: *sender.as_eth(),2137 to: *new_owner.as_eth(),2138 token_id: item_id.into(),2139 })?;2140 Self::deposit_event(RawEvent::Transfer(2141 collection.id,2142 item_id,2143 sender,2144 new_owner,2145 1,2146 ));21472148 Ok(())2149 }21502151 fn set_re_fungible_variable_data(2152 collection: &CollectionHandle<T>,2153 item_id: TokenId,2154 data: Vec<u8>,2155 ) -> DispatchResult {2156 let collection_id = collection.id;2157 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2158 .ok_or(Error::<T>::TokenNotFound)?;21592160 item.variable_data = data;21612162 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21632164 Ok(())2165 }21662167 fn set_nft_variable_data(2168 collection: &CollectionHandle<T>,2169 item_id: TokenId,2170 data: Vec<u8>,2171 ) -> DispatchResult {2172 let collection_id = collection.id;2173 let mut item =2174 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21752176 item.variable_data = data;21772178 <NftItemList<T>>::insert(collection_id, item_id, item);21792180 Ok(())2181 }21822183 #[allow(dead_code)]2184 fn init_collection(item: &Collection<T>) {2185 2186 assert!(2187 item.decimal_points <= MAX_DECIMAL_POINTS,2188 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2189 );2190 assert!(2191 item.name.len() <= 64,2192 "Collection name can not be longer than 63 char"2193 );2194 assert!(2195 item.name.len() <= 256,2196 "Collection description can not be longer than 255 char"2197 );2198 assert!(2199 item.token_prefix.len() <= 16,2200 "Token prefix can not be longer than 15 char"2201 );22022203 2204 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22052206 CreatedCollectionCount::put(next_id);2207 }22082209 #[allow(dead_code)]2210 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2211 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22122213 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22142215 <ItemListIndex>::insert(collection_id, current_index);22162217 2218 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2219 .checked_add(1)2220 .unwrap();2221 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2222 }22232224 #[allow(dead_code)]2225 fn init_fungible_token(2226 collection_id: CollectionId,2227 owner: &T::CrossAccountId,2228 item: &FungibleItemType,2229 ) {2230 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();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(item.value)2239 .unwrap();2240 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241 }22422243 #[allow(dead_code)]2244 fn init_refungible_token(2245 collection_id: CollectionId,2246 item: &ReFungibleItemType<T::CrossAccountId>,2247 ) {2248 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22492250 let value = item.owner.first().unwrap().fraction;2251 let owner = item.owner.first().unwrap().owner.clone();22522253 Self::add_token_index(collection_id, current_index, &owner).unwrap();22542255 <ItemListIndex>::insert(collection_id, current_index);22562257 2258 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2259 .checked_add(value)2260 .unwrap();2261 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2262 }22632264 fn add_token_index(2265 collection_id: CollectionId,2266 item_index: TokenId,2267 owner: &T::CrossAccountId,2268 ) -> DispatchResult {2269 2270 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2271 2272 let count = <AccountItemCount<T>>::get(owner.as_sub());2273 ensure!(2274 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2275 Error::<T>::AddressOwnershipLimitExceeded2276 );22772278 <AccountItemCount<T>>::insert(2279 owner.as_sub(),2280 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2281 );2282 } else {2283 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2284 }22852286 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2287 if list_exists {2288 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2289 let item_contains = list.contains(&item_index.clone());22902291 if !item_contains {2292 list.push(item_index);2293 }22942295 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2296 } else {2297 let itm = vec![item_index];2298 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2299 }23002301 Ok(())2302 }23032304 fn remove_token_index(2305 collection_id: CollectionId,2306 item_index: TokenId,2307 owner: &T::CrossAccountId,2308 ) -> DispatchResult {2309 2310 <AccountItemCount<T>>::insert(2311 owner.as_sub(),2312 <AccountItemCount<T>>::get(owner.as_sub())2313 .checked_sub(1)2314 .ok_or(Error::<T>::NumOverflow)?,2315 );23162317 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2318 if list_exists {2319 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2320 let item_contains = list.contains(&item_index.clone());23212322 if item_contains {2323 list.retain(|&item| item != item_index);2324 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2325 }2326 }23272328 Ok(())2329 }23302331 fn move_token_index(2332 collection_id: CollectionId,2333 item_index: TokenId,2334 old_owner: &T::CrossAccountId,2335 new_owner: &T::CrossAccountId,2336 ) -> DispatchResult {2337 Self::remove_token_index(collection_id, item_index, old_owner)?;2338 Self::add_token_index(collection_id, item_index, new_owner)?;23392340 Ok(())2341 }2342}23432344sp_api::decl_runtime_apis! {2345 pub trait NftApi {2346 2347 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2348 }2349}