123456#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,44 MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,45 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,46 FungibleItemType, ReFungibleItemType,47};4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;66pub mod weights;67use weights::WeightInfo;6869decl_error! {70 71 pub enum Error for Module<T: Config> {72 73 TotalCollectionsLimitExceeded,74 75 CollectionDecimalPointLimitExceeded,76 77 CollectionNameLimitExceeded,78 79 CollectionDescriptionLimitExceeded,80 81 CollectionTokenPrefixLimitExceeded,82 83 CollectionNotFound,84 85 TokenNotFound,86 87 AdminNotFound,88 89 NumOverflow,90 91 AlreadyAdmin,92 93 NoPermission,94 95 ConfirmUnsetSponsorFail,96 97 PublicMintingNotAllowed,98 99 MustBeTokenOwner,100 101 TokenValueTooLow,102 103 NftSizeLimitExceeded,104 105 ApproveNotFound,106 107 TokenValueNotEnough,108 109 ApproveRequired,110 111 AddresNotInWhiteList,112 113 CollectionAdminsLimitExceeded,114 115 AddressOwnershipLimitExceeded,116 117 EmptyArgument,118 119 TokenConstDataLimitExceeded,120 121 TokenVariableDataLimitExceeded,122 123 NotNftDataUsedToMintNftCollectionToken,124 125 NotFungibleDataUsedToMintFungibleCollectionToken,126 127 NotReFungibleDataUsedToMintReFungibleCollectionToken,128 129 UnexpectedCollectionType,130 131 CantStoreMetadataInFungibleTokens,132 133 CollectionTokenLimitExceeded,134 135 AccountTokenLimitExceeded,136 137 CollectionLimitBoundsExceeded,138 139 OwnerPermissionsCantBeReverted,140 141 SchemaDataLimitExceeded,142 143 WrongRefungiblePieces,144 145 BadCreateRefungibleCall,146 147 OutOfGas,148 149 TransferNotAllowed,150 }151}152153#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]154pub struct CollectionHandle<T: Config> {155 pub id: CollectionId,156 collection: Collection<T>,157 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,158}159impl<T: Config> CollectionHandle<T> {160 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {161 <CollectionById<T>>::get(id).map(|collection| Self {162 id,163 collection,164 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(165 eth::collection_id_to_address(id),166 gas_limit,167 ),168 })169 }170 pub fn get(id: CollectionId) -> Option<Self> {171 Self::get_with_gas_limit(id, u64::MAX)172 }173 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {174 self.recorder.log_sub(log)175 }176 fn consume_gas(&self, gas: u64) -> DispatchResult {177 self.recorder.consume_gas_sub(gas)178 }179 pub fn submit_logs(self) -> DispatchResult {180 self.recorder.submit_logs()181 }182 pub fn save(self) -> DispatchResult {183 self.recorder.submit_logs()?;184 <CollectionById<T>>::insert(self.id, self.collection);185 Ok(())186 }187}188impl<T: Config> Deref for CollectionHandle<T> {189 type Target = Collection<T>;190191 fn deref(&self) -> &Self::Target {192 &self.collection193 }194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197 fn deref_mut(&mut self) -> &mut Self::Target {198 &mut self.collection199 }200}201202pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {203 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;204205 206 type WeightInfo: WeightInfo;207208 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;209 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;210211 type CrossAccountId: CrossAccountId<Self::AccountId>;212 type Currency: Currency<Self::AccountId>;213 type CollectionCreationPrice: Get<214 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,215 >;216 type TreasuryAccountId: Get<Self::AccountId>;217}218219type SelfWeightOf<T> = <T as Config>::WeightInfo;220221trait WeightInfoHelpers: WeightInfo {222 fn transfer() -> Weight {223 Self::transfer_nft()224 .max(Self::transfer_fungible())225 .max(Self::transfer_refungible())226 }227 fn transfer_from() -> Weight {228 Self::transfer_from_nft()229 .max(Self::transfer_from_fungible())230 .max(Self::transfer_from_refungible())231 }232 fn approve() -> Weight {233 234 Self::approve_nft()235 }236 fn set_variable_meta_data(data: u32) -> Weight {237 238 Self::set_variable_meta_data_nft(data)239 }240 fn create_item(data: u32) -> Weight {241 Self::create_item_nft(data)242 .max(Self::create_item_fungible())243 .max(Self::create_item_refungible(data))244 }245 fn create_multiple_items(amount: u32) -> Weight {246 Self::create_multiple_items_nft(amount)247 .max(Self::create_multiple_items_fungible(amount))248 .max(Self::create_multiple_items_refungible(amount))249 }250 fn burn_item() -> Weight {251 252 Self::burn_item_nft()253 }254}255impl<T: WeightInfo> WeightInfoHelpers for T {}256257258259260261262263264265266267268269270271272273274275276277278279decl_storage! {280 trait Store for Module<T: Config> as Nft {281282 283 284 CreatedCollectionCount: u32;285 286 ChainVersion: u64;287 288 289 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;290 291292 293 294 295 DestroyedCollectionCount: u32;296 297 298 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;299 300301 302 303 304 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;305 306 307 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;308 309 310 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;311 312313 314 315 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;316317 318 319 320 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;321322 323 324 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;325 326 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;327 328 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;329 330331 332 333 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;334 335336 337 338 339 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;340 341 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;342 343 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;344 345 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;346 347348 349 350 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;351 }352 add_extra_genesis {353 build(|config: &GenesisConfig<T>| {354 355 for (_num, _c) in &config.collection_id {356 <Module<T>>::init_collection(_c);357 }358359 for (_num, _c, _i) in &config.nft_item_id {360 <Module<T>>::init_nft_token(*_c, _i);361 }362363 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {364 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);365 }366367 for (_num, _c, _i) in &config.refungible_item_id {368 <Module<T>>::init_refungible_token(*_c, _i);369 }370 })371 }372}373374decl_event!(375 pub enum Event<T>376 where377 AccountId = <T as frame_system::Config>::AccountId,378 CrossAccountId = <T as Config>::CrossAccountId,379 {380 381 382 383 384 385 386 387 388 389 CollectionCreated(CollectionId, u8, AccountId),390391 392 393 394 395 396 397 398 399 400 ItemCreated(CollectionId, TokenId, CrossAccountId),401402 403 404 405 406 407 408 409 ItemDestroyed(CollectionId, TokenId),410411 412 413 414 415 416 417 418 419 420 421 422 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),423424 425 426 427 428 429 430 431 432 433 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),434 }435);436437decl_module! {438 pub struct Module<T: Config> for enum Call439 where440 origin: T::Origin441 {442 fn deposit_event() = default;443 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;444 type Error = Error<T>;445446 fn on_initialize(_now: T::BlockNumber) -> Weight {447 0448 }449450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 #[weight = <SelfWeightOf<T>>::create_collection()]467 #[transactional]468 pub fn create_collection(origin,469 collection_name: Vec<u16>,470 collection_description: Vec<u16>,471 token_prefix: Vec<u8>,472 mode: CollectionMode) -> DispatchResult {473474 475 let who = ensure_signed(origin)?;476477 478 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();479 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(480 &T::TreasuryAccountId::get(),481 T::CollectionCreationPrice::get(),482 ));483 <T as Config>::Currency::settle(484 &who,485 imbalance,486 WithdrawReasons::TRANSFER,487 ExistenceRequirement::KeepAlive,488 ).map_err(|_| Error::<T>::NoPermission)?;489490 let decimal_points = match mode {491 CollectionMode::Fungible(points) => points,492 _ => 0493 };494495 let created_count = CreatedCollectionCount::get();496 let destroyed_count = DestroyedCollectionCount::get();497498 499 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);500501 502 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);503 ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);504 ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);505 ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);506507 508 let next_id = created_count509 .checked_add(1)510 .ok_or(Error::<T>::NumOverflow)?;511512 CreatedCollectionCount::put(next_id);513514 let limits = CollectionLimits {515 sponsored_data_size: CUSTOM_DATA_LIMIT,516 ..Default::default()517 };518519 520 let new_collection = Collection {521 owner: who.clone(),522 name: collection_name,523 mode: mode.clone(),524 mint_mode: false,525 access: AccessMode::Normal,526 description: collection_description,527 decimal_points,528 token_prefix,529 offchain_schema: Vec::new(),530 schema_version: SchemaVersion::ImageURL,531 sponsorship: SponsorshipState::Disabled,532 variable_on_chain_schema: Vec::new(),533 const_on_chain_schema: Vec::new(),534 limits,535 transfers_enabled: true,536 };537538 539 <CollectionById<T>>::insert(next_id, new_collection);540541 542 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));543544 Ok(())545 }546547 548 549 550 551 552 553 554 555 556 #[weight = <SelfWeightOf<T>>::destroy_collection()]557 #[transactional]558 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {559560 let sender = ensure_signed(origin)?;561 let collection = Self::get_collection(collection_id)?;562 Self::check_owner_permissions(&collection, &sender)?;563 if !collection.limits.owner_can_destroy {564 fail!(Error::<T>::NoPermission);565 }566567 <AddressTokens<T>>::remove_prefix(collection_id, None);568 <Allowances<T>>::remove_prefix(collection_id, None);569 <Balance<T>>::remove_prefix(collection_id, None);570 <ItemListIndex>::remove(collection_id);571 <AdminList<T>>::remove(collection_id);572 <CollectionById<T>>::remove(collection_id);573 <WhiteList<T>>::remove_prefix(collection_id, None);574575 <NftItemList<T>>::remove_prefix(collection_id, None);576 <FungibleItemList<T>>::remove_prefix(collection_id, None);577 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);578579 <NftTransferBasket<T>>::remove_prefix(collection_id, None);580 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);581 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);582583 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);584585 DestroyedCollectionCount::put(DestroyedCollectionCount::get()586 .checked_add(1)587 .ok_or(Error::<T>::NumOverflow)?);588589 Ok(())590 }591592 593 594 595 596 597 598 599 600 601 602 603 604 #[weight = <SelfWeightOf<T>>::add_to_white_list()]605 #[transactional]606 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{607608 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);609 let collection = Self::get_collection(collection_id)?;610611 Self::toggle_white_list_internal(612 &sender,613 &collection,614 &address,615 true,616 )?;617618 Ok(())619 }620621 622 623 624 625 626 627 628 629 630 631 632 633 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]634 #[transactional]635 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{636637 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);638 let collection = Self::get_collection(collection_id)?;639640 Self::toggle_white_list_internal(641 &sender,642 &collection,643 &address,644 false,645 )?;646647 Ok(())648 }649650 651 652 653 654 655 656 657 658 659 660 661 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]662 #[transactional]663 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult664 {665 let sender = ensure_signed(origin)?;666667 let mut target_collection = Self::get_collection(collection_id)?;668 Self::check_owner_permissions(&target_collection, &sender)?;669 target_collection.access = mode;670 target_collection.save()671 }672673 674 675 676 677 678 679 680 681 682 683 684 685 686 #[weight = <SelfWeightOf<T>>::set_mint_permission()]687 #[transactional]688 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult689 {690 let sender = ensure_signed(origin)?;691692 let mut target_collection = Self::get_collection(collection_id)?;693 Self::check_owner_permissions(&target_collection, &sender)?;694 target_collection.mint_mode = mint_permission;695 target_collection.save()696 }697698 699 700 701 702 703 704 705 706 707 708 709 #[weight = <SelfWeightOf<T>>::change_collection_owner()]710 #[transactional]711 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {712713 let sender = ensure_signed(origin)?;714 let mut target_collection = Self::get_collection(collection_id)?;715 Self::check_owner_permissions(&target_collection, &sender)?;716 target_collection.owner = new_owner;717 target_collection.save()718 }719720 721 722 723 724 725 726 727 728 729 730 731 732 733 #[weight = <SelfWeightOf<T>>::add_collection_admin()]734 #[transactional]735 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {736 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);737 let collection = Self::get_collection(collection_id)?;738 Self::check_owner_or_admin_permissions(&collection, &sender)?;739 let mut admin_arr = <AdminList<T>>::get(collection_id);740741 match admin_arr.binary_search(&new_admin_id) {742 Ok(_) => {},743 Err(idx) => {744 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);745 admin_arr.insert(idx, new_admin_id);746 <AdminList<T>>::insert(collection_id, admin_arr);747 }748 }749 Ok(())750 }751752 753 754 755 756 757 758 759 760 761 762 763 764 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]765 #[transactional]766 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {767 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);768 let collection = Self::get_collection(collection_id)?;769 Self::check_owner_or_admin_permissions(&collection, &sender)?;770 let mut admin_arr = <AdminList<T>>::get(collection_id);771772 if let Ok(idx) = admin_arr.binary_search(&account_id) {773 admin_arr.remove(idx);774 <AdminList<T>>::insert(collection_id, admin_arr);775 }776 Ok(())777 }778779 780 781 782 783 784 785 786 787 788 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]789 #[transactional]790 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {791 let sender = ensure_signed(origin)?;792 let mut target_collection = Self::get_collection(collection_id)?;793 Self::check_owner_permissions(&target_collection, &sender)?;794795 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);796 target_collection.save()797 }798799 800 801 802 803 804 805 806 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]807 #[transactional]808 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {809 let sender = ensure_signed(origin)?;810811 let mut target_collection = Self::get_collection(collection_id)?;812 ensure!(813 target_collection.sponsorship.pending_sponsor() == Some(&sender),814 Error::<T>::ConfirmUnsetSponsorFail815 );816817 target_collection.sponsorship = SponsorshipState::Confirmed(sender);818 target_collection.save()819 }820821 822 823 824 825 826 827 828 829 830 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]831 #[transactional]832 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {833 let sender = ensure_signed(origin)?;834835 let mut target_collection = Self::get_collection(collection_id)?;836 Self::check_owner_permissions(&target_collection, &sender)?;837838 target_collection.sponsorship = SponsorshipState::Disabled;839 target_collection.save()840 }841842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865866 #[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]867 #[transactional]868 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {869 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);870 let collection = Self::get_collection(collection_id)?;871872 Self::create_item_internal(&sender, &collection, &owner, data)?;873874 collection.submit_logs()875 }876877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 #[weight = <SelfWeightOf<T>>::create_multiple_items(items_data.len() as u32)]896 #[transactional]897 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {898899 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);900 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);901 let collection = Self::get_collection(collection_id)?;902903 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;904905 collection.submit_logs()906 }907908 909910 911 912 913 914 915 916 917 918 919 920 921 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]922 #[transactional]923 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {924925 let sender = ensure_signed(origin)?;926 let mut target_collection = Self::get_collection(collection_id)?;927928 Self::check_owner_permissions(&target_collection, &sender)?;929930 target_collection.transfers_enabled = value;931 target_collection.save()932 }933934 935 936 937 938 939 940 941 942 943 944 945 946 947 #[weight = <SelfWeightOf<T>>::burn_item()]948 #[transactional]949 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {950951 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952 let target_collection = Self::get_collection(collection_id)?;953954 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;955956 target_collection.submit_logs()957 }958959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 #[weight = <SelfWeightOf<T>>::transfer()]983 #[transactional]984 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {985 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);986 let collection = Self::get_collection(collection_id)?;987988 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;989990 collection.submit_logs()991 }992993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 #[weight = <SelfWeightOf<T>>::approve()]1009 #[transactional]1010 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1011 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1012 let collection = Self::get_collection(collection_id)?;10131014 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10151016 collection.submit_logs()1017 }10181019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 #[weight = <SelfWeightOf<T>>::transfer_from()]1039 #[transactional]1040 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1041 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1042 let collection = Self::get_collection(collection_id)?;10431044 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10451046 collection.submit_logs()1047 }1048 1049 1050 1051 1052 10531054 10551056 10571058 1059 10601061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1074 #[transactional]1075 pub fn set_variable_meta_data (1076 origin,1077 collection_id: CollectionId,1078 item_id: TokenId,1079 data: Vec<u8>1080 ) -> DispatchResult {1081 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10821083 let collection = Self::get_collection(collection_id)?;10841085 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10861087 Ok(())1088 }10891090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 #[weight = <SelfWeightOf<T>>::set_schema_version()]1105 #[transactional]1106 pub fn set_schema_version(1107 origin,1108 collection_id: CollectionId,1109 version: SchemaVersion1110 ) -> DispatchResult {1111 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1112 let mut target_collection = Self::get_collection(collection_id)?;1113 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1114 target_collection.schema_version = version;1115 target_collection.save()1116 }11171118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1131 #[transactional]1132 pub fn set_offchain_schema(1133 origin,1134 collection_id: CollectionId,1135 schema: Vec<u8>1136 ) -> DispatchResult {1137 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1138 let mut target_collection = Self::get_collection(collection_id)?;1139 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11401141 1142 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11431144 target_collection.offchain_schema = schema;1145 target_collection.save()1146 }11471148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1161 #[transactional]1162 pub fn set_const_on_chain_schema (1163 origin,1164 collection_id: CollectionId,1165 schema: Vec<u8>1166 ) -> DispatchResult {1167 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1168 let mut target_collection = Self::get_collection(collection_id)?;1169 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11701171 1172 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11731174 target_collection.const_on_chain_schema = schema;1175 target_collection.save()1176 }11771178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1191 #[transactional]1192 pub fn set_variable_on_chain_schema (1193 origin,1194 collection_id: CollectionId,1195 schema: Vec<u8>1196 ) -> DispatchResult {1197 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1198 let mut target_collection = Self::get_collection(collection_id)?;1199 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12001201 1202 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12031204 target_collection.variable_on_chain_schema = schema;1205 target_collection.save()1206 }12071208 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1209 #[transactional]1210 pub fn set_collection_limits(1211 origin,1212 collection_id: u32,1213 new_limits: CollectionLimits<T::BlockNumber>,1214 ) -> DispatchResult {1215 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1216 let mut target_collection = Self::get_collection(collection_id)?;1217 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1218 let old_limits = &target_collection.limits;12191220 1221 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1222 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1223 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1224 Error::<T>::CollectionLimitBoundsExceeded);12251226 1227 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1228 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12291230 ensure!(1231 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1232 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1233 Error::<T>::OwnerPermissionsCantBeReverted,1234 );12351236 target_collection.limits = new_limits;12371238 target_collection.save()1239 }1240 }1241}12421243impl<T: Config> Module<T> {1244 pub fn create_item_internal(1245 sender: &T::CrossAccountId,1246 collection: &CollectionHandle<T>,1247 owner: &T::CrossAccountId,1248 data: CreateItemData,1249 ) -> DispatchResult {1250 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1251 Self::validate_create_item_args(collection, &data)?;1252 Self::create_item_no_validation(collection, owner, data)?;12531254 Ok(())1255 }12561257 pub fn transfer_internal(1258 sender: &T::CrossAccountId,1259 recipient: &T::CrossAccountId,1260 target_collection: &CollectionHandle<T>,1261 item_id: TokenId,1262 value: u128,1263 ) -> DispatchResult {1264 target_collection.consume_gas(2000000)?;1265 1266 Self::is_correct_transfer(target_collection, recipient)?;12671268 1269 ensure!(1270 Self::is_item_owner(sender, target_collection, item_id)1271 || Self::is_owner_or_admin_permissions(target_collection, sender),1272 Error::<T>::NoPermission1273 );12741275 if target_collection.access == AccessMode::WhiteList {1276 Self::check_white_list(target_collection, sender)?;1277 Self::check_white_list(target_collection, recipient)?;1278 }12791280 match target_collection.mode {1281 CollectionMode::NFT => Self::transfer_nft(1282 target_collection,1283 item_id,1284 sender.clone(),1285 recipient.clone(),1286 )?,1287 CollectionMode::Fungible(_) => {1288 Self::transfer_fungible(target_collection, value, sender, recipient)?1289 }1290 CollectionMode::ReFungible => Self::transfer_refungible(1291 target_collection,1292 item_id,1293 value,1294 sender.clone(),1295 recipient.clone(),1296 )?,1297 _ => (),1298 };12991300 Self::deposit_event(RawEvent::Transfer(1301 target_collection.id,1302 item_id,1303 sender.clone(),1304 recipient.clone(),1305 value,1306 ));13071308 Ok(())1309 }13101311 pub fn approve_internal(1312 sender: &T::CrossAccountId,1313 spender: &T::CrossAccountId,1314 collection: &CollectionHandle<T>,1315 item_id: TokenId,1316 amount: u128,1317 ) -> DispatchResult {1318 collection.consume_gas(2000000)?;1319 Self::token_exists(collection, item_id)?;13201321 1322 let bypasses_limits = collection.limits.owner_can_transfer1323 && Self::is_owner_or_admin_permissions(collection, sender);13241325 let allowance_limit = if bypasses_limits {1326 None1327 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1328 Some(amount)1329 } else {1330 fail!(Error::<T>::NoPermission);1331 };13321333 if collection.access == AccessMode::WhiteList {1334 Self::check_white_list(collection, sender)?;1335 Self::check_white_list(collection, spender)?;1336 }13371338 let allowance: u128 = amount1339 .checked_add(<Allowances<T>>::get(1340 collection.id,1341 (item_id, sender.as_sub(), spender.as_sub()),1342 ))1343 .ok_or(Error::<T>::NumOverflow)?;1344 if let Some(limit) = allowance_limit {1345 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1346 }1347 <Allowances<T>>::insert(1348 collection.id,1349 (item_id, sender.as_sub(), spender.as_sub()),1350 allowance,1351 );13521353 if matches!(collection.mode, CollectionMode::NFT) {1354 1355 collection.log(ERC721Events::Approval {1356 owner: *sender.as_eth(),1357 approved: *spender.as_eth(),1358 token_id: item_id.into(),1359 })?;1360 }13611362 if matches!(collection.mode, CollectionMode::Fungible(_)) {1363 1364 collection.log(ERC20Events::Approval {1365 owner: *sender.as_eth(),1366 spender: *spender.as_eth(),1367 value: allowance.into(),1368 })?;1369 }13701371 Self::deposit_event(RawEvent::Approved(1372 collection.id,1373 item_id,1374 sender.clone(),1375 spender.clone(),1376 allowance,1377 ));1378 Ok(())1379 }13801381 pub fn transfer_from_internal(1382 sender: &T::CrossAccountId,1383 from: &T::CrossAccountId,1384 recipient: &T::CrossAccountId,1385 collection: &CollectionHandle<T>,1386 item_id: TokenId,1387 amount: u128,1388 ) -> DispatchResult {1389 collection.consume_gas(2000000)?;1390 1391 let approval: u128 =1392 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13931394 1395 Self::is_correct_transfer(collection, recipient)?;13961397 1398 ensure!(1399 approval >= amount1400 || (collection.limits.owner_can_transfer1401 && Self::is_owner_or_admin_permissions(collection, sender)),1402 Error::<T>::NoPermission1403 );14041405 if collection.access == AccessMode::WhiteList {1406 Self::check_white_list(collection, sender)?;1407 Self::check_white_list(collection, recipient)?;1408 }14091410 1411 let allowance = approval.saturating_sub(amount);1412 if allowance > 0 {1413 <Allowances<T>>::insert(1414 collection.id,1415 (item_id, from.as_sub(), sender.as_sub()),1416 allowance,1417 );1418 } else {1419 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1420 }14211422 match collection.mode {1423 CollectionMode::NFT => {1424 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1425 }1426 CollectionMode::Fungible(_) => {1427 Self::transfer_fungible(collection, amount, from, recipient)?1428 }1429 CollectionMode::ReFungible => Self::transfer_refungible(1430 collection,1431 item_id,1432 amount,1433 from.clone(),1434 recipient.clone(),1435 )?,1436 _ => (),1437 };14381439 if matches!(collection.mode, CollectionMode::Fungible(_)) {1440 collection.log(ERC20Events::Approval {1441 owner: *from.as_eth(),1442 spender: *sender.as_eth(),1443 value: allowance.into(),1444 })?;1445 }14461447 Ok(())1448 }14491450 pub fn set_variable_meta_data_internal(1451 sender: &T::CrossAccountId,1452 collection: &CollectionHandle<T>,1453 item_id: TokenId,1454 data: Vec<u8>,1455 ) -> DispatchResult {1456 Self::token_exists(collection, item_id)?;14571458 ensure!(1459 CUSTOM_DATA_LIMIT >= data.len() as u32,1460 Error::<T>::TokenVariableDataLimitExceeded1461 );14621463 1464 ensure!(1465 Self::is_item_owner(sender, collection, item_id)1466 || Self::is_owner_or_admin_permissions(collection, sender),1467 Error::<T>::NoPermission1468 );14691470 match collection.mode {1471 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1472 CollectionMode::ReFungible => {1473 Self::set_re_fungible_variable_data(collection, item_id, data)?1474 }1475 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1476 _ => fail!(Error::<T>::UnexpectedCollectionType),1477 };14781479 Ok(())1480 }14811482 pub fn create_multiple_items_internal(1483 sender: &T::CrossAccountId,1484 collection: &CollectionHandle<T>,1485 owner: &T::CrossAccountId,1486 items_data: Vec<CreateItemData>,1487 ) -> DispatchResult {1488 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14891490 for data in &items_data {1491 Self::validate_create_item_args(collection, data)?;1492 }1493 for data in &items_data {1494 Self::create_item_no_validation(collection, owner, data.clone())?;1495 }14961497 Ok(())1498 }14991500 pub fn burn_item_internal(1501 sender: &T::CrossAccountId,1502 collection: &CollectionHandle<T>,1503 item_id: TokenId,1504 value: u128,1505 ) -> DispatchResult {1506 ensure!(1507 Self::is_item_owner(sender, collection, item_id)1508 || (collection.limits.owner_can_transfer1509 && Self::is_owner_or_admin_permissions(collection, sender)),1510 Error::<T>::NoPermission1511 );15121513 if collection.access == AccessMode::WhiteList {1514 Self::check_white_list(collection, sender)?;1515 }15161517 match collection.mode {1518 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1519 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1520 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1521 _ => (),1522 };15231524 Ok(())1525 }15261527 pub fn toggle_white_list_internal(1528 sender: &T::CrossAccountId,1529 collection: &CollectionHandle<T>,1530 address: &T::CrossAccountId,1531 whitelisted: bool,1532 ) -> DispatchResult {1533 Self::check_owner_or_admin_permissions(collection, sender)?;15341535 if whitelisted {1536 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1537 } else {1538 <WhiteList<T>>::remove(collection.id, address.as_sub());1539 }15401541 Ok(())1542 }15431544 fn is_correct_transfer(1545 collection: &CollectionHandle<T>,1546 recipient: &T::CrossAccountId,1547 ) -> DispatchResult {1548 let collection_id = collection.id;15491550 1551 let account_items: u32 =1552 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1553 ensure!(1554 collection.limits.account_token_ownership_limit > account_items,1555 Error::<T>::AccountTokenLimitExceeded1556 );15571558 1559 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15601561 Ok(())1562 }15631564 fn can_create_items_in_collection(1565 collection: &CollectionHandle<T>,1566 sender: &T::CrossAccountId,1567 owner: &T::CrossAccountId,1568 amount: u32,1569 ) -> DispatchResult {1570 let collection_id = collection.id;15711572 1573 let total_items: u32 = ItemListIndex::get(collection_id)1574 .checked_add(amount)1575 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1576 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1577 as u32)1578 .checked_add(amount)1579 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1580 ensure!(1581 collection.limits.token_limit >= total_items,1582 Error::<T>::CollectionTokenLimitExceeded1583 );1584 ensure!(1585 collection.limits.account_token_ownership_limit >= account_items,1586 Error::<T>::AccountTokenLimitExceeded1587 );15881589 if !Self::is_owner_or_admin_permissions(collection, sender) {1590 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1591 Self::check_white_list(collection, owner)?;1592 Self::check_white_list(collection, sender)?;1593 }15941595 Ok(())1596 }15971598 fn validate_create_item_args(1599 target_collection: &CollectionHandle<T>,1600 data: &CreateItemData,1601 ) -> DispatchResult {1602 match target_collection.mode {1603 CollectionMode::NFT => {1604 if !matches!(data, CreateItemData::NFT(_)) {1605 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1606 }1607 }1608 CollectionMode::Fungible(_) => {1609 if !matches!(data, CreateItemData::Fungible(_)) {1610 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1611 }1612 }1613 CollectionMode::ReFungible => {1614 if let CreateItemData::ReFungible(data) = data {1615 1616 ensure!(1617 data.pieces <= MAX_REFUNGIBLE_PIECES,1618 Error::<T>::WrongRefungiblePieces1619 );1620 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1621 } else {1622 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1623 }1624 }1625 _ => {1626 fail!(Error::<T>::UnexpectedCollectionType);1627 }1628 };16291630 Ok(())1631 }16321633 fn create_item_no_validation(1634 collection: &CollectionHandle<T>,1635 owner: &T::CrossAccountId,1636 data: CreateItemData,1637 ) -> DispatchResult {1638 match data {1639 CreateItemData::NFT(data) => {1640 let item = NftItemType {1641 owner: owner.clone(),1642 const_data: data.const_data.into_inner(),1643 variable_data: data.variable_data.into_inner(),1644 };16451646 Self::add_nft_item(collection, item)?;1647 }1648 CreateItemData::Fungible(data) => {1649 Self::add_fungible_item(collection, owner, data.value)?;1650 }1651 CreateItemData::ReFungible(data) => {1652 let owner_list = vec![Ownership {1653 owner: owner.clone(),1654 fraction: data.pieces,1655 }];16561657 let item = ReFungibleItemType {1658 owner: owner_list,1659 const_data: data.const_data.into_inner(),1660 variable_data: data.variable_data.into_inner(),1661 };16621663 Self::add_refungible_item(collection, item)?;1664 }1665 };16661667 Ok(())1668 }16691670 fn add_fungible_item(1671 collection: &CollectionHandle<T>,1672 owner: &T::CrossAccountId,1673 value: u128,1674 ) -> DispatchResult {1675 let collection_id = collection.id;16761677 1678 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16791680 1681 let item = FungibleItemType {1682 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1683 };1684 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16851686 1687 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1688 .checked_add(value)1689 .ok_or(Error::<T>::NumOverflow)?;1690 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16911692 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1693 Ok(())1694 }16951696 fn add_refungible_item(1697 collection: &CollectionHandle<T>,1698 item: ReFungibleItemType<T::CrossAccountId>,1699 ) -> DispatchResult {1700 let collection_id = collection.id;17011702 let current_index = <ItemListIndex>::get(collection_id)1703 .checked_add(1)1704 .ok_or(Error::<T>::NumOverflow)?;1705 let itemcopy = item.clone();17061707 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1708 let item_owner = item.owner.first().expect("only one owner is defined");17091710 let value = item_owner.fraction;1711 let owner = item_owner.owner.clone();17121713 Self::add_token_index(collection_id, current_index, &owner)?;17141715 <ItemListIndex>::insert(collection_id, current_index);1716 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17171718 1719 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1720 .checked_add(value)1721 .ok_or(Error::<T>::NumOverflow)?;1722 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17231724 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1725 Ok(())1726 }17271728 fn add_nft_item(1729 collection: &CollectionHandle<T>,1730 item: NftItemType<T::CrossAccountId>,1731 ) -> DispatchResult {1732 let collection_id = collection.id;17331734 let current_index = <ItemListIndex>::get(collection_id)1735 .checked_add(1)1736 .ok_or(Error::<T>::NumOverflow)?;17371738 let item_owner = item.owner.clone();1739 Self::add_token_index(collection_id, current_index, &item.owner)?;17401741 <ItemListIndex>::insert(collection_id, current_index);1742 <NftItemList<T>>::insert(collection_id, current_index, item);17431744 1745 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1746 .checked_add(1)1747 .ok_or(Error::<T>::NumOverflow)?;1748 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17491750 collection.log(ERC721Events::Transfer {1751 from: H160::default(),1752 to: *item_owner.as_eth(),1753 token_id: current_index.into(),1754 })?;1755 Self::deposit_event(RawEvent::ItemCreated(1756 collection_id,1757 current_index,1758 item_owner,1759 ));1760 Ok(())1761 }17621763 fn burn_refungible_item(1764 collection: &CollectionHandle<T>,1765 item_id: TokenId,1766 owner: &T::CrossAccountId,1767 ) -> DispatchResult {1768 let collection_id = collection.id;17691770 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1771 .ok_or(Error::<T>::TokenNotFound)?;1772 let rft_balance = token1773 .owner1774 .iter()1775 .find(|&i| i.owner == *owner)1776 .ok_or(Error::<T>::TokenNotFound)?;1777 Self::remove_token_index(collection_id, item_id, owner)?;17781779 1780 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1781 .checked_sub(rft_balance.fraction)1782 .ok_or(Error::<T>::NumOverflow)?;1783 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17841785 1786 let index = token1787 .owner1788 .iter()1789 .position(|i| i.owner == *owner)1790 .expect("owned item is exists");1791 token.owner.remove(index);1792 let owner_count = token.owner.len();17931794 1795 if owner_count == 0 {1796 <ReFungibleItemList<T>>::remove(collection_id, item_id);1797 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1798 } else {1799 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1800 }18011802 Ok(())1803 }18041805 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1806 let collection_id = collection.id;18071808 let item =1809 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1810 Self::remove_token_index(collection_id, item_id, &item.owner)?;18111812 1813 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1814 .checked_sub(1)1815 .ok_or(Error::<T>::NumOverflow)?;1816 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1817 <NftItemList<T>>::remove(collection_id, item_id);1818 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18191820 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1821 Ok(())1822 }18231824 fn burn_fungible_item(1825 owner: &T::CrossAccountId,1826 collection: &CollectionHandle<T>,1827 value: u128,1828 ) -> DispatchResult {1829 let collection_id = collection.id;18301831 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1832 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18331834 1835 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1836 .checked_sub(value)1837 .ok_or(Error::<T>::NumOverflow)?;1838 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18391840 if balance.value - value > 0 {1841 balance.value -= value;1842 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1843 } else {1844 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1845 }18461847 collection.log(ERC20Events::Transfer {1848 from: *owner.as_eth(),1849 to: H160::default(),1850 value: value.into(),1851 })?;1852 Ok(())1853 }18541855 pub fn get_collection(1856 collection_id: CollectionId,1857 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1858 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1859 }18601861 fn check_owner_permissions(1862 target_collection: &CollectionHandle<T>,1863 subject: &T::AccountId,1864 ) -> DispatchResult {1865 ensure!(1866 *subject == target_collection.owner,1867 Error::<T>::NoPermission1868 );18691870 Ok(())1871 }18721873 fn is_owner_or_admin_permissions(1874 collection: &CollectionHandle<T>,1875 subject: &T::CrossAccountId,1876 ) -> bool {1877 *subject.as_sub() == collection.owner1878 || <AdminList<T>>::get(collection.id).contains(subject)1879 }18801881 fn check_owner_or_admin_permissions(1882 collection: &CollectionHandle<T>,1883 subject: &T::CrossAccountId,1884 ) -> DispatchResult {1885 ensure!(1886 Self::is_owner_or_admin_permissions(collection, subject),1887 Error::<T>::NoPermission1888 );18891890 Ok(())1891 }18921893 fn owned_amount(1894 subject: &T::CrossAccountId,1895 target_collection: &CollectionHandle<T>,1896 item_id: TokenId,1897 ) -> Option<u128> {1898 let collection_id = target_collection.id;18991900 match target_collection.mode {1901 CollectionMode::NFT => {1902 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1903 }1904 CollectionMode::Fungible(_) => {1905 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1906 }1907 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1908 .owner1909 .iter()1910 .find(|i| i.owner == *subject)1911 .map(|i| i.fraction),1912 CollectionMode::Invalid => None,1913 }1914 }19151916 fn is_item_owner(1917 subject: &T::CrossAccountId,1918 target_collection: &CollectionHandle<T>,1919 item_id: TokenId,1920 ) -> bool {1921 match target_collection.mode {1922 CollectionMode::Fungible(_) => true,1923 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1924 }1925 }19261927 fn check_white_list(1928 collection: &CollectionHandle<T>,1929 address: &T::CrossAccountId,1930 ) -> DispatchResult {1931 let collection_id = collection.id;19321933 let mes = Error::<T>::AddresNotInWhiteList;1934 ensure!(1935 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1936 mes1937 );19381939 Ok(())1940 }19411942 1943 1944 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1945 let collection_id = target_collection.id;1946 let exists = match target_collection.mode {1947 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1948 CollectionMode::Fungible(_) => true,1949 CollectionMode::ReFungible => {1950 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1951 }1952 _ => false,1953 };19541955 ensure!(exists, Error::<T>::TokenNotFound);1956 Ok(())1957 }19581959 fn transfer_fungible(1960 collection: &CollectionHandle<T>,1961 value: u128,1962 owner: &T::CrossAccountId,1963 recipient: &T::CrossAccountId,1964 ) -> DispatchResult {1965 let collection_id = collection.id;19661967 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1968 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19691970 1971 Self::add_fungible_item(collection, recipient, value)?;19721973 1974 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19751976 1977 if balance.value == value {1978 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1979 } else {1980 balance.value -= value;1981 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1982 }19831984 collection.log(ERC20Events::Transfer {1985 from: *owner.as_eth(),1986 to: *recipient.as_eth(),1987 value: value.into(),1988 })?;1989 Self::deposit_event(RawEvent::Transfer(1990 collection.id,1991 1,1992 owner.clone(),1993 recipient.clone(),1994 value,1995 ));19961997 Ok(())1998 }19992000 fn transfer_refungible(2001 collection: &CollectionHandle<T>,2002 item_id: TokenId,2003 value: u128,2004 owner: T::CrossAccountId,2005 new_owner: T::CrossAccountId,2006 ) -> DispatchResult {2007 let collection_id = collection.id;2008 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2009 .ok_or(Error::<T>::TokenNotFound)?;20102011 let item = full_item2012 .owner2013 .iter()2014 .find(|i| i.owner == owner)2015 .ok_or(Error::<T>::TokenNotFound)?;2016 let amount = item.fraction;20172018 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20192020 2021 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2022 .checked_sub(value)2023 .ok_or(Error::<T>::NumOverflow)?;2024 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20252026 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2027 .checked_add(value)2028 .ok_or(Error::<T>::NumOverflow)?;2029 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20302031 let old_owner = item.owner.clone();2032 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20332034 let mut new_full_item = full_item.clone();2035 2036 if amount == value && !new_owner_has_account {2037 2038 2039 new_full_item2040 .owner2041 .iter_mut()2042 .find(|i| i.owner == owner)2043 .expect("old owner does present in refungible")2044 .owner = new_owner.clone();2045 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20462047 2048 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2049 } else {2050 new_full_item2051 .owner2052 .iter_mut()2053 .find(|i| i.owner == owner)2054 .expect("old owner does present in refungible")2055 .fraction -= value;20562057 2058 if new_owner_has_account {2059 2060 new_full_item2061 .owner2062 .iter_mut()2063 .find(|i| i.owner == new_owner)2064 .expect("new owner has account")2065 .fraction += value;2066 } else {2067 2068 new_full_item.owner.push(Ownership {2069 owner: new_owner.clone(),2070 fraction: value,2071 });2072 Self::add_token_index(collection_id, item_id, &new_owner)?;2073 }20742075 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2076 }20772078 Self::deposit_event(RawEvent::Transfer(2079 collection.id,2080 item_id,2081 owner,2082 new_owner,2083 amount,2084 ));20852086 Ok(())2087 }20882089 fn transfer_nft(2090 collection: &CollectionHandle<T>,2091 item_id: TokenId,2092 sender: T::CrossAccountId,2093 new_owner: T::CrossAccountId,2094 ) -> DispatchResult {2095 let collection_id = collection.id;2096 let mut item =2097 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20982099 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21002101 2102 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2103 .checked_sub(1)2104 .ok_or(Error::<T>::NumOverflow)?;2105 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21062107 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2108 .checked_add(1)2109 .ok_or(Error::<T>::NumOverflow)?;2110 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21112112 2113 let old_owner = item.owner.clone();2114 item.owner = new_owner.clone();2115 <NftItemList<T>>::insert(collection_id, item_id, item);21162117 2118 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21192120 collection.log(ERC721Events::Transfer {2121 from: *sender.as_eth(),2122 to: *new_owner.as_eth(),2123 token_id: item_id.into(),2124 })?;2125 Self::deposit_event(RawEvent::Transfer(2126 collection.id,2127 item_id,2128 sender,2129 new_owner,2130 1,2131 ));21322133 Ok(())2134 }21352136 fn set_re_fungible_variable_data(2137 collection: &CollectionHandle<T>,2138 item_id: TokenId,2139 data: Vec<u8>,2140 ) -> DispatchResult {2141 let collection_id = collection.id;2142 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2143 .ok_or(Error::<T>::TokenNotFound)?;21442145 item.variable_data = data;21462147 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21482149 Ok(())2150 }21512152 fn set_nft_variable_data(2153 collection: &CollectionHandle<T>,2154 item_id: TokenId,2155 data: Vec<u8>,2156 ) -> DispatchResult {2157 let collection_id = collection.id;2158 let mut item =2159 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21602161 item.variable_data = data;21622163 <NftItemList<T>>::insert(collection_id, item_id, item);21642165 Ok(())2166 }21672168 #[allow(dead_code)]2169 fn init_collection(item: &Collection<T>) {2170 2171 assert!(2172 item.decimal_points <= MAX_DECIMAL_POINTS,2173 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2174 );2175 assert!(2176 item.name.len() <= 64,2177 "Collection name can not be longer than 63 char"2178 );2179 assert!(2180 item.name.len() <= 256,2181 "Collection description can not be longer than 255 char"2182 );2183 assert!(2184 item.token_prefix.len() <= 16,2185 "Token prefix can not be longer than 15 char"2186 );21872188 2189 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21902191 CreatedCollectionCount::put(next_id);2192 }21932194 #[allow(dead_code)]2195 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2196 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21972198 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21992200 <ItemListIndex>::insert(collection_id, current_index);22012202 2203 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2204 .checked_add(1)2205 .unwrap();2206 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2207 }22082209 #[allow(dead_code)]2210 fn init_fungible_token(2211 collection_id: CollectionId,2212 owner: &T::CrossAccountId,2213 item: &FungibleItemType,2214 ) {2215 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22162217 Self::add_token_index(collection_id, current_index, owner).unwrap();22182219 <ItemListIndex>::insert(collection_id, current_index);22202221 2222 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2223 .checked_add(item.value)2224 .unwrap();2225 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2226 }22272228 #[allow(dead_code)]2229 fn init_refungible_token(2230 collection_id: CollectionId,2231 item: &ReFungibleItemType<T::CrossAccountId>,2232 ) {2233 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22342235 let value = item.owner.first().unwrap().fraction;2236 let owner = item.owner.first().unwrap().owner.clone();22372238 Self::add_token_index(collection_id, current_index, &owner).unwrap();22392240 <ItemListIndex>::insert(collection_id, current_index);22412242 2243 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2244 .checked_add(value)2245 .unwrap();2246 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2247 }22482249 fn add_token_index(2250 collection_id: CollectionId,2251 item_index: TokenId,2252 owner: &T::CrossAccountId,2253 ) -> DispatchResult {2254 2255 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2256 2257 let count = <AccountItemCount<T>>::get(owner.as_sub());2258 ensure!(2259 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2260 Error::<T>::AddressOwnershipLimitExceeded2261 );22622263 <AccountItemCount<T>>::insert(2264 owner.as_sub(),2265 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2266 );2267 } else {2268 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2269 }22702271 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2272 if list_exists {2273 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2274 let item_contains = list.contains(&item_index.clone());22752276 if !item_contains {2277 list.push(item_index);2278 }22792280 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2281 } else {2282 let itm = vec![item_index];2283 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2284 }22852286 Ok(())2287 }22882289 fn remove_token_index(2290 collection_id: CollectionId,2291 item_index: TokenId,2292 owner: &T::CrossAccountId,2293 ) -> DispatchResult {2294 2295 <AccountItemCount<T>>::insert(2296 owner.as_sub(),2297 <AccountItemCount<T>>::get(owner.as_sub())2298 .checked_sub(1)2299 .ok_or(Error::<T>::NumOverflow)?,2300 );23012302 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2303 if list_exists {2304 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2305 let item_contains = list.contains(&item_index.clone());23062307 if item_contains {2308 list.retain(|&item| item != item_index);2309 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2310 }2311 }23122313 Ok(())2314 }23152316 fn move_token_index(2317 collection_id: CollectionId,2318 item_index: TokenId,2319 old_owner: &T::CrossAccountId,2320 new_owner: &T::CrossAccountId,2321 ) -> DispatchResult {2322 Self::remove_token_index(collection_id, item_index, old_owner)?;2323 Self::add_token_index(collection_id, item_index, new_owner)?;23242325 Ok(())2326 }2327}23282329sp_api::decl_runtime_apis! {2330 pub trait NftApi {2331 2332 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2333 }2334}