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 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,43 FungibleItemType, ReFungibleItemType,44};4546#[cfg(test)]47mod mock;4849#[cfg(test)]50mod tests;5152mod default_weights;53mod eth;54mod sponsorship;55pub use sponsorship::NftSponsorshipHandler;56pub use eth::sponsoring::NftEthSponsorshipHandler;5758pub use eth::NftErcSupport;59pub use eth::account::*;60use eth::erc::{ERC20Events, ERC721Events};6162#[cfg(feature = "runtime-benchmarks")]63mod benchmarking;6465pub trait WeightInfo {66 fn create_collection() -> Weight;67 fn destroy_collection() -> Weight;68 fn add_to_white_list() -> Weight;69 fn remove_from_white_list() -> Weight;70 fn set_public_access_mode() -> Weight;71 fn set_mint_permission() -> Weight;72 fn change_collection_owner() -> Weight;73 fn add_collection_admin() -> Weight;74 fn remove_collection_admin() -> Weight;75 fn set_collection_sponsor() -> Weight;76 fn confirm_sponsorship() -> Weight;77 fn remove_collection_sponsor() -> Weight;78 fn create_item(s: usize) -> Weight;79 fn burn_item() -> Weight;80 fn transfer() -> Weight;81 fn approve() -> Weight;82 fn transfer_from() -> Weight;83 fn set_offchain_schema() -> Weight;84 fn set_const_on_chain_schema() -> Weight;85 fn set_variable_on_chain_schema() -> Weight;86 fn set_variable_meta_data() -> Weight;87 fn enable_contract_sponsoring() -> Weight;88 fn set_schema_version() -> Weight;89 fn set_chain_limits() -> Weight;90 fn set_contract_sponsoring_rate_limit() -> Weight;91 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;92 fn toggle_contract_white_list() -> Weight;93 fn add_to_contract_white_list() -> Weight;94 fn remove_from_contract_white_list() -> Weight;95 fn set_collection_limits() -> Weight;96}9798decl_error! {99 100 pub enum Error for Module<T: Config> {101 102 TotalCollectionsLimitExceeded,103 104 CollectionDecimalPointLimitExceeded,105 106 CollectionNameLimitExceeded,107 108 CollectionDescriptionLimitExceeded,109 110 CollectionTokenPrefixLimitExceeded,111 112 CollectionNotFound,113 114 TokenNotFound,115 116 AdminNotFound,117 118 NumOverflow,119 120 AlreadyAdmin,121 122 NoPermission,123 124 ConfirmUnsetSponsorFail,125 126 PublicMintingNotAllowed,127 128 MustBeTokenOwner,129 130 TokenValueTooLow,131 132 NftSizeLimitExceeded,133 134 ApproveNotFound,135 136 TokenValueNotEnough,137 138 ApproveRequired,139 140 AddresNotInWhiteList,141 142 CollectionAdminsLimitExceeded,143 144 AddressOwnershipLimitExceeded,145 146 EmptyArgument,147 148 TokenConstDataLimitExceeded,149 150 TokenVariableDataLimitExceeded,151 152 NotNftDataUsedToMintNftCollectionToken,153 154 NotFungibleDataUsedToMintFungibleCollectionToken,155 156 NotReFungibleDataUsedToMintReFungibleCollectionToken,157 158 UnexpectedCollectionType,159 160 CantStoreMetadataInFungibleTokens,161 162 CollectionTokenLimitExceeded,163 164 AccountTokenLimitExceeded,165 166 CollectionLimitBoundsExceeded,167 168 OwnerPermissionsCantBeReverted,169 170 SchemaDataLimitExceeded,171 172 WrongRefungiblePieces,173 174 BadCreateRefungibleCall,175 176 OutOfGas,177 178 TransferNotAllowed,179 }180}181182#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]183pub struct CollectionHandle<T: Config> {184 pub id: CollectionId,185 collection: Collection<T>,186 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,187}188impl<T: Config> CollectionHandle<T> {189 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {190 <CollectionById<T>>::get(id).map(|collection| Self {191 id,192 collection,193 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(194 eth::collection_id_to_address(id),195 gas_limit,196 ),197 })198 }199 pub fn get(id: CollectionId) -> Option<Self> {200 Self::get_with_gas_limit(id, u64::MAX)201 }202 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {203 self.recorder.log_sub(log)204 }205 fn consume_gas(&self, gas: u64) -> DispatchResult {206 self.recorder.consume_gas_sub(gas)207 }208 pub fn submit_logs(self) -> DispatchResult {209 self.recorder.submit_logs()210 }211 pub fn save(self) -> DispatchResult {212 self.recorder.submit_logs()?;213 <CollectionById<T>>::insert(self.id, self.collection);214 Ok(())215 }216}217impl<T: Config> Deref for CollectionHandle<T> {218 type Target = Collection<T>;219220 fn deref(&self) -> &Self::Target {221 &self.collection222 }223}224225impl<T: Config> DerefMut for CollectionHandle<T> {226 fn deref_mut(&mut self) -> &mut Self::Target {227 &mut self.collection228 }229}230231pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {232 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;233234 235 type WeightInfo: WeightInfo;236237 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;238 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;239240 type CrossAccountId: CrossAccountId<Self::AccountId>;241 type Currency: Currency<Self::AccountId>;242 type CollectionCreationPrice: Get<243 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,244 >;245 type TreasuryAccountId: Get<Self::AccountId>;246 type ChainLimits: ChainLimits;247}248249pub type ChainLimitsOf<T> = <T as Config>::ChainLimits;250#[macro_export]251macro_rules! limit {252 ($config:ty, $limit:ident) => {253 <$crate::ChainLimitsOf<$config> as nft_data_structs::ChainLimits>::$limit254 }255}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 type Error = Error<T>;444445 fn on_initialize(_now: T::BlockNumber) -> Weight {446 0447 }448449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 #[weight = <T as Config>::WeightInfo::create_collection()]466 #[transactional]467 pub fn create_collection(origin,468 collection_name: Vec<u16>,469 collection_description: Vec<u16>,470 token_prefix: Vec<u8>,471 mode: CollectionMode) -> DispatchResult {472473 474 let who = ensure_signed(origin)?;475476 477 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();478 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(479 &T::TreasuryAccountId::get(),480 T::CollectionCreationPrice::get(),481 ));482 <T as Config>::Currency::settle(483 &who,484 imbalance,485 WithdrawReasons::TRANSFER,486 ExistenceRequirement::KeepAlive,487 ).map_err(|_| Error::<T>::NoPermission)?;488489 let decimal_points = match mode {490 CollectionMode::Fungible(points) => points,491 _ => 0492 };493494 let created_count = CreatedCollectionCount::get();495 let destroyed_count = DestroyedCollectionCount::get();496497 498 ensure!(created_count - destroyed_count < <limit!(T, CollectionNumberLimit)>::get(), Error::<T>::TotalCollectionsLimitExceeded);499500 501 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);502 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);504 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);505506 507 let next_id = created_count508 .checked_add(1)509 .ok_or(Error::<T>::NumOverflow)?;510511 CreatedCollectionCount::put(next_id);512513 let limits = CollectionLimits {514 sponsored_data_size: <limit!(T, CustomDataLimit)>::get(),515 ..Default::default()516 };517518 519 let new_collection = Collection {520 owner: who.clone(),521 name: collection_name,522 mode: mode.clone(),523 mint_mode: false,524 access: AccessMode::Normal,525 description: collection_description,526 decimal_points,527 token_prefix,528 offchain_schema: Vec::new(),529 schema_version: SchemaVersion::ImageURL,530 sponsorship: SponsorshipState::Disabled,531 variable_on_chain_schema: Vec::new(),532 const_on_chain_schema: Vec::new(),533 limits,534 transfers_enabled: true,535 };536537 538 <CollectionById<T>>::insert(next_id, new_collection);539540 541 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));542543 Ok(())544 }545546 547 548 549 550 551 552 553 554 555 #[weight = <T as Config>::WeightInfo::destroy_collection()]556 #[transactional]557 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {558559 let sender = ensure_signed(origin)?;560 let collection = Self::get_collection(collection_id)?;561 Self::check_owner_permissions(&collection, &sender)?;562 if !collection.limits.owner_can_destroy {563 fail!(Error::<T>::NoPermission);564 }565566 <AddressTokens<T>>::remove_prefix(collection_id, None);567 <Allowances<T>>::remove_prefix(collection_id, None);568 <Balance<T>>::remove_prefix(collection_id, None);569 <ItemListIndex>::remove(collection_id);570 <AdminList<T>>::remove(collection_id);571 <CollectionById<T>>::remove(collection_id);572 <WhiteList<T>>::remove_prefix(collection_id, None);573574 <NftItemList<T>>::remove_prefix(collection_id, None);575 <FungibleItemList<T>>::remove_prefix(collection_id, None);576 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);577578 <NftTransferBasket<T>>::remove_prefix(collection_id, None);579 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);580 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);581582 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);583584 DestroyedCollectionCount::put(DestroyedCollectionCount::get()585 .checked_add(1)586 .ok_or(Error::<T>::NumOverflow)?);587588 Ok(())589 }590591 592 593 594 595 596 597 598 599 600 601 602 603 #[weight = <T as Config>::WeightInfo::add_to_white_list()]604 #[transactional]605 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{606607 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);608 let collection = Self::get_collection(collection_id)?;609610 Self::toggle_white_list_internal(611 &sender,612 &collection,613 &address,614 true,615 )?;616617 Ok(())618 }619620 621 622 623 624 625 626 627 628 629 630 631 632 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]633 #[transactional]634 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{635636 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);637 let collection = Self::get_collection(collection_id)?;638639 Self::toggle_white_list_internal(640 &sender,641 &collection,642 &address,643 false,644 )?;645646 Ok(())647 }648649 650 651 652 653 654 655 656 657 658 659 660 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]661 #[transactional]662 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult663 {664 let sender = ensure_signed(origin)?;665666 let mut target_collection = Self::get_collection(collection_id)?;667 Self::check_owner_permissions(&target_collection, &sender)?;668 target_collection.access = mode;669 target_collection.save()670 }671672 673 674 675 676 677 678 679 680 681 682 683 684 685 #[weight = <T as Config>::WeightInfo::set_mint_permission()]686 #[transactional]687 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult688 {689 let sender = ensure_signed(origin)?;690691 let mut target_collection = Self::get_collection(collection_id)?;692 Self::check_owner_permissions(&target_collection, &sender)?;693 target_collection.mint_mode = mint_permission;694 target_collection.save()695 }696697 698 699 700 701 702 703 704 705 706 707 708 #[weight = <T as Config>::WeightInfo::change_collection_owner()]709 #[transactional]710 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {711712 let sender = ensure_signed(origin)?;713 let mut target_collection = Self::get_collection(collection_id)?;714 Self::check_owner_permissions(&target_collection, &sender)?;715 target_collection.owner = new_owner;716 target_collection.save()717 }718719 720 721 722 723 724 725 726 727 728 729 730 731 732 #[weight = <T as Config>::WeightInfo::add_collection_admin()]733 #[transactional]734 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {735 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);736 let collection = Self::get_collection(collection_id)?;737 Self::check_owner_or_admin_permissions(&collection, &sender)?;738 let mut admin_arr = <AdminList<T>>::get(collection_id);739740 match admin_arr.binary_search(&new_admin_id) {741 Ok(_) => {},742 Err(idx) => {743 ensure!(admin_arr.len() < <limit!(T, CollectionAdminsLimit)>::get() as usize, Error::<T>::CollectionAdminsLimitExceeded);744 admin_arr.insert(idx, new_admin_id);745 <AdminList<T>>::insert(collection_id, admin_arr);746 }747 }748 Ok(())749 }750751 752 753 754 755 756 757 758 759 760 761 762 763 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]764 #[transactional]765 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {766 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);767 let collection = Self::get_collection(collection_id)?;768 Self::check_owner_or_admin_permissions(&collection, &sender)?;769 let mut admin_arr = <AdminList<T>>::get(collection_id);770771 if let Ok(idx) = admin_arr.binary_search(&account_id) {772 admin_arr.remove(idx);773 <AdminList<T>>::insert(collection_id, admin_arr);774 }775 Ok(())776 }777778 779 780 781 782 783 784 785 786 787 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]788 #[transactional]789 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {790 let sender = ensure_signed(origin)?;791 let mut target_collection = Self::get_collection(collection_id)?;792 Self::check_owner_permissions(&target_collection, &sender)?;793794 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);795 target_collection.save()796 }797798 799 800 801 802 803 804 805 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]806 #[transactional]807 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {808 let sender = ensure_signed(origin)?;809810 let mut target_collection = Self::get_collection(collection_id)?;811 ensure!(812 target_collection.sponsorship.pending_sponsor() == Some(&sender),813 Error::<T>::ConfirmUnsetSponsorFail814 );815816 target_collection.sponsorship = SponsorshipState::Confirmed(sender);817 target_collection.save()818 }819820 821 822 823 824 825 826 827 828 829 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]830 #[transactional]831 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {832 let sender = ensure_signed(origin)?;833834 let mut target_collection = Self::get_collection(collection_id)?;835 Self::check_owner_permissions(&target_collection, &sender)?;836837 target_collection.sponsorship = SponsorshipState::Disabled;838 target_collection.save()839 }840841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864865 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]866 #[transactional]867 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData<ChainLimitsOf<T>>) -> DispatchResult {868 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);869 let collection = Self::get_collection(collection_id)?;870871 Self::create_item_internal(&sender, &collection, &owner, data)?;872873 collection.submit_logs()874 }875876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()895 .map(|data| { data.data_size() })896 .sum())]897 #[transactional]898 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData<ChainLimitsOf<T>>>) -> DispatchResult {899900 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);901 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902 let collection = Self::get_collection(collection_id)?;903904 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;905906 collection.submit_logs()907 }908909 910911 912 913 914 915 916 917 918 919 920 921 922 #[weight = <T as Config>::WeightInfo::burn_item()]923 #[transactional]924 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {925926 let sender = ensure_signed(origin)?;927 let mut target_collection = Self::get_collection(collection_id)?;928929 Self::check_owner_permissions(&target_collection, &sender)?;930931 target_collection.transfers_enabled = value;932 target_collection.save()933 }934935 936 937 938 939 940 941 942 943 944 945 946 947 948 #[weight = <T as Config>::WeightInfo::burn_item()]949 #[transactional]950 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {951952 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);953 let target_collection = Self::get_collection(collection_id)?;954955 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;956957 target_collection.submit_logs()958 }959960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 #[weight = <T as Config>::WeightInfo::transfer()]984 #[transactional]985 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {986 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);987 let collection = Self::get_collection(collection_id)?;988989 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;990991 collection.submit_logs()992 }993994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 #[weight = <T as Config>::WeightInfo::approve()]1010 #[transactional]1011 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1012 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013 let collection = Self::get_collection(collection_id)?;10141015 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10161017 collection.submit_logs()1018 }10191020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 #[weight = <T as Config>::WeightInfo::transfer_from()]1040 #[transactional]1041 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1042 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1043 let collection = Self::get_collection(collection_id)?;10441045 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10461047 collection.submit_logs()1048 }1049 1050 1051 1052 1053 10541055 10561057 10581059 1060 10611062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1075 #[transactional]1076 pub fn set_variable_meta_data (1077 origin,1078 collection_id: CollectionId,1079 item_id: TokenId,1080 data: Vec<u8>1081 ) -> DispatchResult {1082 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10831084 let collection = Self::get_collection(collection_id)?;10851086 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10871088 Ok(())1089 }10901091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 #[weight = <T as Config>::WeightInfo::set_schema_version()]1106 #[transactional]1107 pub fn set_schema_version(1108 origin,1109 collection_id: CollectionId,1110 version: SchemaVersion1111 ) -> DispatchResult {1112 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1113 let mut target_collection = Self::get_collection(collection_id)?;1114 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1115 target_collection.schema_version = version;1116 target_collection.save()1117 }11181119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1132 #[transactional]1133 pub fn set_offchain_schema(1134 origin,1135 collection_id: CollectionId,1136 schema: Vec<u8>1137 ) -> DispatchResult {1138 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1139 let mut target_collection = Self::get_collection(collection_id)?;1140 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11411142 1143 ensure!(schema.len() as u32 <= <limit!(T, OffchainSchemaLimit)>::get(), "");11441145 target_collection.offchain_schema = schema;1146 target_collection.save()1147 }11481149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1162 #[transactional]1163 pub fn set_const_on_chain_schema (1164 origin,1165 collection_id: CollectionId,1166 schema: Vec<u8>1167 ) -> DispatchResult {1168 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1169 let mut target_collection = Self::get_collection(collection_id)?;1170 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11711172 1173 ensure!(schema.len() as u32 <= <limit!(T, ConstOnChainSchemaLimit)>::get(), "");11741175 target_collection.const_on_chain_schema = schema;1176 target_collection.save()1177 }11781179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1192 #[transactional]1193 pub fn set_variable_on_chain_schema (1194 origin,1195 collection_id: CollectionId,1196 schema: Vec<u8>1197 ) -> DispatchResult {1198 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1199 let mut target_collection = Self::get_collection(collection_id)?;1200 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12011202 1203 ensure!(schema.len() as u32 <= <limit!(T, VariableOnChainSchemaLimit)>::get(), "");12041205 target_collection.variable_on_chain_schema = schema;1206 target_collection.save()1207 }12081209 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1210 #[transactional]1211 pub fn set_collection_limits(1212 origin,1213 collection_id: u32,1214 new_limits: CollectionLimits<T::BlockNumber>,1215 ) -> DispatchResult {1216 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1217 let mut target_collection = Self::get_collection(collection_id)?;1218 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1219 let old_limits = &target_collection.limits;12201221 1222 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1223 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1224 new_limits.sponsored_data_size <= <ChainLimitsOf<T> as ChainLimits>::CustomDataLimit::get(),1225 Error::<T>::CollectionLimitBoundsExceeded);12261227 1228 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1229 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12301231 ensure!(1232 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1233 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1234 Error::<T>::OwnerPermissionsCantBeReverted,1235 );12361237 target_collection.limits = new_limits;12381239 target_collection.save()1240 }1241 }1242}12431244impl<T: Config> Module<T> {1245 pub fn create_item_internal(1246 sender: &T::CrossAccountId,1247 collection: &CollectionHandle<T>,1248 owner: &T::CrossAccountId,1249 data: CreateItemData<ChainLimitsOf<T>>,1250 ) -> DispatchResult {1251 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1252 Self::validate_create_item_args(collection, &data)?;1253 Self::create_item_no_validation(collection, owner, data)?;12541255 Ok(())1256 }12571258 pub fn transfer_internal(1259 sender: &T::CrossAccountId,1260 recipient: &T::CrossAccountId,1261 target_collection: &CollectionHandle<T>,1262 item_id: TokenId,1263 value: u128,1264 ) -> DispatchResult {1265 target_collection.consume_gas(2000000)?;1266 1267 Self::is_correct_transfer(target_collection, recipient)?;12681269 1270 ensure!(1271 Self::is_item_owner(sender, target_collection, item_id)1272 || Self::is_owner_or_admin_permissions(target_collection, sender),1273 Error::<T>::NoPermission1274 );12751276 if target_collection.access == AccessMode::WhiteList {1277 Self::check_white_list(target_collection, sender)?;1278 Self::check_white_list(target_collection, recipient)?;1279 }12801281 match target_collection.mode {1282 CollectionMode::NFT => Self::transfer_nft(1283 target_collection,1284 item_id,1285 sender.clone(),1286 recipient.clone(),1287 )?,1288 CollectionMode::Fungible(_) => {1289 Self::transfer_fungible(target_collection, value, sender, recipient)?1290 }1291 CollectionMode::ReFungible => Self::transfer_refungible(1292 target_collection,1293 item_id,1294 value,1295 sender.clone(),1296 recipient.clone(),1297 )?,1298 _ => (),1299 };13001301 Self::deposit_event(RawEvent::Transfer(1302 target_collection.id,1303 item_id,1304 sender.clone(),1305 recipient.clone(),1306 value,1307 ));13081309 Ok(())1310 }13111312 pub fn approve_internal(1313 sender: &T::CrossAccountId,1314 spender: &T::CrossAccountId,1315 collection: &CollectionHandle<T>,1316 item_id: TokenId,1317 amount: u128,1318 ) -> DispatchResult {1319 collection.consume_gas(2000000)?;1320 Self::token_exists(collection, item_id)?;13211322 1323 let bypasses_limits = collection.limits.owner_can_transfer1324 && Self::is_owner_or_admin_permissions(collection, sender);13251326 let allowance_limit = if bypasses_limits {1327 None1328 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1329 Some(amount)1330 } else {1331 fail!(Error::<T>::NoPermission);1332 };13331334 if collection.access == AccessMode::WhiteList {1335 Self::check_white_list(collection, sender)?;1336 Self::check_white_list(collection, spender)?;1337 }13381339 let allowance: u128 = amount1340 .checked_add(<Allowances<T>>::get(1341 collection.id,1342 (item_id, sender.as_sub(), spender.as_sub()),1343 ))1344 .ok_or(Error::<T>::NumOverflow)?;1345 if let Some(limit) = allowance_limit {1346 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1347 }1348 <Allowances<T>>::insert(1349 collection.id,1350 (item_id, sender.as_sub(), spender.as_sub()),1351 allowance,1352 );13531354 if matches!(collection.mode, CollectionMode::NFT) {1355 1356 collection.log(ERC721Events::Approval {1357 owner: *sender.as_eth(),1358 approved: *spender.as_eth(),1359 token_id: item_id.into(),1360 })?;1361 }13621363 if matches!(collection.mode, CollectionMode::Fungible(_)) {1364 1365 collection.log(ERC20Events::Approval {1366 owner: *sender.as_eth(),1367 spender: *spender.as_eth(),1368 value: allowance.into(),1369 })?;1370 }13711372 Self::deposit_event(RawEvent::Approved(1373 collection.id,1374 item_id,1375 sender.clone(),1376 spender.clone(),1377 allowance,1378 ));1379 Ok(())1380 }13811382 pub fn transfer_from_internal(1383 sender: &T::CrossAccountId,1384 from: &T::CrossAccountId,1385 recipient: &T::CrossAccountId,1386 collection: &CollectionHandle<T>,1387 item_id: TokenId,1388 amount: u128,1389 ) -> DispatchResult {1390 collection.consume_gas(2000000)?;1391 1392 let approval: u128 =1393 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13941395 1396 Self::is_correct_transfer(collection, recipient)?;13971398 1399 ensure!(1400 approval >= amount1401 || (collection.limits.owner_can_transfer1402 && Self::is_owner_or_admin_permissions(collection, sender)),1403 Error::<T>::NoPermission1404 );14051406 if collection.access == AccessMode::WhiteList {1407 Self::check_white_list(collection, sender)?;1408 Self::check_white_list(collection, recipient)?;1409 }14101411 1412 let allowance = approval.saturating_sub(amount);1413 if allowance > 0 {1414 <Allowances<T>>::insert(1415 collection.id,1416 (item_id, from.as_sub(), sender.as_sub()),1417 allowance,1418 );1419 } else {1420 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1421 }14221423 match collection.mode {1424 CollectionMode::NFT => {1425 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1426 }1427 CollectionMode::Fungible(_) => {1428 Self::transfer_fungible(collection, amount, from, recipient)?1429 }1430 CollectionMode::ReFungible => Self::transfer_refungible(1431 collection,1432 item_id,1433 amount,1434 from.clone(),1435 recipient.clone(),1436 )?,1437 _ => (),1438 };14391440 if matches!(collection.mode, CollectionMode::Fungible(_)) {1441 collection.log(ERC20Events::Approval {1442 owner: *from.as_eth(),1443 spender: *sender.as_eth(),1444 value: allowance.into(),1445 })?;1446 }14471448 Ok(())1449 }14501451 pub fn set_variable_meta_data_internal(1452 sender: &T::CrossAccountId,1453 collection: &CollectionHandle<T>,1454 item_id: TokenId,1455 data: Vec<u8>,1456 ) -> DispatchResult {1457 Self::token_exists(collection, item_id)?;14581459 ensure!(1460 <limit!(T, CustomDataLimit)>::get() >= data.len() as u32,1461 Error::<T>::TokenVariableDataLimitExceeded1462 );14631464 1465 ensure!(1466 Self::is_item_owner(sender, collection, item_id)1467 || Self::is_owner_or_admin_permissions(collection, sender),1468 Error::<T>::NoPermission1469 );14701471 match collection.mode {1472 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1473 CollectionMode::ReFungible => {1474 Self::set_re_fungible_variable_data(collection, item_id, data)?1475 }1476 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1477 _ => fail!(Error::<T>::UnexpectedCollectionType),1478 };14791480 Ok(())1481 }14821483 pub fn create_multiple_items_internal(1484 sender: &T::CrossAccountId,1485 collection: &CollectionHandle<T>,1486 owner: &T::CrossAccountId,1487 items_data: Vec<CreateItemData<ChainLimitsOf<T>>>,1488 ) -> DispatchResult {1489 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14901491 for data in &items_data {1492 Self::validate_create_item_args(collection, data)?;1493 }1494 for data in &items_data {1495 Self::create_item_no_validation(collection, owner, data.clone())?;1496 }14971498 Ok(())1499 }15001501 pub fn burn_item_internal(1502 sender: &T::CrossAccountId,1503 collection: &CollectionHandle<T>,1504 item_id: TokenId,1505 value: u128,1506 ) -> DispatchResult {1507 ensure!(1508 Self::is_item_owner(sender, collection, item_id)1509 || (collection.limits.owner_can_transfer1510 && Self::is_owner_or_admin_permissions(collection, sender)),1511 Error::<T>::NoPermission1512 );15131514 if collection.access == AccessMode::WhiteList {1515 Self::check_white_list(collection, sender)?;1516 }15171518 match collection.mode {1519 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1520 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1521 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1522 _ => (),1523 };15241525 Ok(())1526 }15271528 pub fn toggle_white_list_internal(1529 sender: &T::CrossAccountId,1530 collection: &CollectionHandle<T>,1531 address: &T::CrossAccountId,1532 whitelisted: bool,1533 ) -> DispatchResult {1534 Self::check_owner_or_admin_permissions(collection, sender)?;15351536 if whitelisted {1537 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1538 } else {1539 <WhiteList<T>>::remove(collection.id, address.as_sub());1540 }15411542 Ok(())1543 }15441545 fn is_correct_transfer(1546 collection: &CollectionHandle<T>,1547 recipient: &T::CrossAccountId,1548 ) -> DispatchResult {1549 let collection_id = collection.id;15501551 1552 let account_items: u32 =1553 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1554 ensure!(1555 collection.limits.account_token_ownership_limit > account_items,1556 Error::<T>::AccountTokenLimitExceeded1557 );15581559 1560 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15611562 Ok(())1563 }15641565 fn can_create_items_in_collection(1566 collection: &CollectionHandle<T>,1567 sender: &T::CrossAccountId,1568 owner: &T::CrossAccountId,1569 amount: u32,1570 ) -> DispatchResult {1571 let collection_id = collection.id;15721573 1574 let total_items: u32 = ItemListIndex::get(collection_id)1575 .checked_add(amount)1576 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1577 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1578 as u32)1579 .checked_add(amount)1580 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1581 ensure!(1582 collection.limits.token_limit >= total_items,1583 Error::<T>::CollectionTokenLimitExceeded1584 );1585 ensure!(1586 collection.limits.account_token_ownership_limit >= account_items,1587 Error::<T>::AccountTokenLimitExceeded1588 );15891590 if !Self::is_owner_or_admin_permissions(collection, sender) {1591 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1592 Self::check_white_list(collection, owner)?;1593 Self::check_white_list(collection, sender)?;1594 }15951596 Ok(())1597 }15981599 fn validate_create_item_args(1600 target_collection: &CollectionHandle<T>,1601 data: &CreateItemData<ChainLimitsOf<T>>,1602 ) -> DispatchResult {1603 match target_collection.mode {1604 CollectionMode::NFT => {1605 if let CreateItemData::NFT(data) = data {1606 1607 ensure!(1608 <limit!(T, CustomDataLimit)>::get() >= data.const_data.len() as u32,1609 Error::<T>::TokenConstDataLimitExceeded1610 );1611 ensure!(1612 <limit!(T, CustomDataLimit)>::get() >= data.variable_data.len() as u32,1613 Error::<T>::TokenVariableDataLimitExceeded1614 );1615 } else {1616 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1617 }1618 }1619 CollectionMode::Fungible(_) => {1620 if let CreateItemData::Fungible(_) = data {1621 } else {1622 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1623 }1624 }1625 CollectionMode::ReFungible => {1626 if let CreateItemData::ReFungible(data) = data {1627 1628 ensure!(1629 <limit!(T, CustomDataLimit)>::get() >= data.const_data.len() as u32,1630 Error::<T>::TokenConstDataLimitExceeded1631 );1632 ensure!(1633 <limit!(T, CustomDataLimit)>::get() >= data.variable_data.len() as u32,1634 Error::<T>::TokenVariableDataLimitExceeded1635 );16361637 1638 ensure!(1639 data.pieces <= MAX_REFUNGIBLE_PIECES,1640 Error::<T>::WrongRefungiblePieces1641 );1642 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1643 } else {1644 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1645 }1646 }1647 _ => {1648 fail!(Error::<T>::UnexpectedCollectionType);1649 }1650 };16511652 Ok(())1653 }16541655 fn create_item_no_validation(1656 collection: &CollectionHandle<T>,1657 owner: &T::CrossAccountId,1658 data: CreateItemData<ChainLimitsOf<T>>,1659 ) -> DispatchResult {1660 match data {1661 CreateItemData::NFT(data) => {1662 let item = NftItemType {1663 owner: owner.clone(),1664 const_data: data.const_data.into_inner(),1665 variable_data: data.variable_data.into_inner(),1666 };16671668 Self::add_nft_item(collection, item)?;1669 }1670 CreateItemData::Fungible(data) => {1671 Self::add_fungible_item(collection, owner, data.value)?;1672 }1673 CreateItemData::ReFungible(data) => {1674 let owner_list = vec![Ownership {1675 owner: owner.clone(),1676 fraction: data.pieces,1677 }];16781679 let item = ReFungibleItemType {1680 owner: owner_list,1681 const_data: data.const_data.into_inner(),1682 variable_data: data.variable_data.into_inner(),1683 };16841685 Self::add_refungible_item(collection, item)?;1686 }1687 };16881689 Ok(())1690 }16911692 fn add_fungible_item(1693 collection: &CollectionHandle<T>,1694 owner: &T::CrossAccountId,1695 value: u128,1696 ) -> DispatchResult {1697 let collection_id = collection.id;16981699 1700 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17011702 1703 let item = FungibleItemType {1704 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1705 };1706 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17071708 1709 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1710 .checked_add(value)1711 .ok_or(Error::<T>::NumOverflow)?;1712 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17131714 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1715 Ok(())1716 }17171718 fn add_refungible_item(1719 collection: &CollectionHandle<T>,1720 item: ReFungibleItemType<T::CrossAccountId>,1721 ) -> DispatchResult {1722 let collection_id = collection.id;17231724 let current_index = <ItemListIndex>::get(collection_id)1725 .checked_add(1)1726 .ok_or(Error::<T>::NumOverflow)?;1727 let itemcopy = item.clone();17281729 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1730 let item_owner = item.owner.first().expect("only one owner is defined");17311732 let value = item_owner.fraction;1733 let owner = item_owner.owner.clone();17341735 Self::add_token_index(collection_id, current_index, &owner)?;17361737 <ItemListIndex>::insert(collection_id, current_index);1738 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17391740 1741 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1742 .checked_add(value)1743 .ok_or(Error::<T>::NumOverflow)?;1744 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17451746 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1747 Ok(())1748 }17491750 fn add_nft_item(1751 collection: &CollectionHandle<T>,1752 item: NftItemType<T::CrossAccountId>,1753 ) -> DispatchResult {1754 let collection_id = collection.id;17551756 let current_index = <ItemListIndex>::get(collection_id)1757 .checked_add(1)1758 .ok_or(Error::<T>::NumOverflow)?;17591760 let item_owner = item.owner.clone();1761 Self::add_token_index(collection_id, current_index, &item.owner)?;17621763 <ItemListIndex>::insert(collection_id, current_index);1764 <NftItemList<T>>::insert(collection_id, current_index, item);17651766 1767 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1768 .checked_add(1)1769 .ok_or(Error::<T>::NumOverflow)?;1770 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17711772 collection.log(ERC721Events::Transfer {1773 from: H160::default(),1774 to: *item_owner.as_eth(),1775 token_id: current_index.into(),1776 })?;1777 Self::deposit_event(RawEvent::ItemCreated(1778 collection_id,1779 current_index,1780 item_owner,1781 ));1782 Ok(())1783 }17841785 fn burn_refungible_item(1786 collection: &CollectionHandle<T>,1787 item_id: TokenId,1788 owner: &T::CrossAccountId,1789 ) -> DispatchResult {1790 let collection_id = collection.id;17911792 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1793 .ok_or(Error::<T>::TokenNotFound)?;1794 let rft_balance = token1795 .owner1796 .iter()1797 .find(|&i| i.owner == *owner)1798 .ok_or(Error::<T>::TokenNotFound)?;1799 Self::remove_token_index(collection_id, item_id, owner)?;18001801 1802 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1803 .checked_sub(rft_balance.fraction)1804 .ok_or(Error::<T>::NumOverflow)?;1805 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18061807 1808 let index = token1809 .owner1810 .iter()1811 .position(|i| i.owner == *owner)1812 .expect("owned item is exists");1813 token.owner.remove(index);1814 let owner_count = token.owner.len();18151816 1817 if owner_count == 0 {1818 <ReFungibleItemList<T>>::remove(collection_id, item_id);1819 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1820 } else {1821 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1822 }18231824 Ok(())1825 }18261827 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1828 let collection_id = collection.id;18291830 let item =1831 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1832 Self::remove_token_index(collection_id, item_id, &item.owner)?;18331834 1835 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1836 .checked_sub(1)1837 .ok_or(Error::<T>::NumOverflow)?;1838 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1839 <NftItemList<T>>::remove(collection_id, item_id);1840 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18411842 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1843 Ok(())1844 }18451846 fn burn_fungible_item(1847 owner: &T::CrossAccountId,1848 collection: &CollectionHandle<T>,1849 value: u128,1850 ) -> DispatchResult {1851 let collection_id = collection.id;18521853 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1854 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18551856 1857 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1858 .checked_sub(value)1859 .ok_or(Error::<T>::NumOverflow)?;1860 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18611862 if balance.value - value > 0 {1863 balance.value -= value;1864 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1865 } else {1866 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1867 }18681869 collection.log(ERC20Events::Transfer {1870 from: *owner.as_eth(),1871 to: H160::default(),1872 value: value.into(),1873 })?;1874 Ok(())1875 }18761877 pub fn get_collection(1878 collection_id: CollectionId,1879 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1880 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1881 }18821883 fn check_owner_permissions(1884 target_collection: &CollectionHandle<T>,1885 subject: &T::AccountId,1886 ) -> DispatchResult {1887 ensure!(1888 *subject == target_collection.owner,1889 Error::<T>::NoPermission1890 );18911892 Ok(())1893 }18941895 fn is_owner_or_admin_permissions(1896 collection: &CollectionHandle<T>,1897 subject: &T::CrossAccountId,1898 ) -> bool {1899 *subject.as_sub() == collection.owner1900 || <AdminList<T>>::get(collection.id).contains(subject)1901 }19021903 fn check_owner_or_admin_permissions(1904 collection: &CollectionHandle<T>,1905 subject: &T::CrossAccountId,1906 ) -> DispatchResult {1907 ensure!(1908 Self::is_owner_or_admin_permissions(collection, subject),1909 Error::<T>::NoPermission1910 );19111912 Ok(())1913 }19141915 fn owned_amount(1916 subject: &T::CrossAccountId,1917 target_collection: &CollectionHandle<T>,1918 item_id: TokenId,1919 ) -> Option<u128> {1920 let collection_id = target_collection.id;19211922 match target_collection.mode {1923 CollectionMode::NFT => {1924 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1925 }1926 CollectionMode::Fungible(_) => {1927 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1928 }1929 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1930 .owner1931 .iter()1932 .find(|i| i.owner == *subject)1933 .map(|i| i.fraction),1934 CollectionMode::Invalid => None,1935 }1936 }19371938 fn is_item_owner(1939 subject: &T::CrossAccountId,1940 target_collection: &CollectionHandle<T>,1941 item_id: TokenId,1942 ) -> bool {1943 match target_collection.mode {1944 CollectionMode::Fungible(_) => true,1945 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1946 }1947 }19481949 fn check_white_list(1950 collection: &CollectionHandle<T>,1951 address: &T::CrossAccountId,1952 ) -> DispatchResult {1953 let collection_id = collection.id;19541955 let mes = Error::<T>::AddresNotInWhiteList;1956 ensure!(1957 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1958 mes1959 );19601961 Ok(())1962 }19631964 1965 1966 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1967 let collection_id = target_collection.id;1968 let exists = match target_collection.mode {1969 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1970 CollectionMode::Fungible(_) => true,1971 CollectionMode::ReFungible => {1972 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1973 }1974 _ => false,1975 };19761977 ensure!(exists, Error::<T>::TokenNotFound);1978 Ok(())1979 }19801981 fn transfer_fungible(1982 collection: &CollectionHandle<T>,1983 value: u128,1984 owner: &T::CrossAccountId,1985 recipient: &T::CrossAccountId,1986 ) -> DispatchResult {1987 let collection_id = collection.id;19881989 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1990 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19911992 1993 Self::add_fungible_item(collection, recipient, value)?;19941995 1996 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19971998 1999 if balance.value == value {2000 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2001 } else {2002 balance.value -= value;2003 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2004 }20052006 collection.log(ERC20Events::Transfer {2007 from: *owner.as_eth(),2008 to: *recipient.as_eth(),2009 value: value.into(),2010 })?;2011 Self::deposit_event(RawEvent::Transfer(2012 collection.id,2013 1,2014 owner.clone(),2015 recipient.clone(),2016 value,2017 ));20182019 Ok(())2020 }20212022 fn transfer_refungible(2023 collection: &CollectionHandle<T>,2024 item_id: TokenId,2025 value: u128,2026 owner: T::CrossAccountId,2027 new_owner: T::CrossAccountId,2028 ) -> DispatchResult {2029 let collection_id = collection.id;2030 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2031 .ok_or(Error::<T>::TokenNotFound)?;20322033 let item = full_item2034 .owner2035 .iter()2036 .find(|i| i.owner == owner)2037 .ok_or(Error::<T>::TokenNotFound)?;2038 let amount = item.fraction;20392040 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20412042 2043 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2044 .checked_sub(value)2045 .ok_or(Error::<T>::NumOverflow)?;2046 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20472048 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2049 .checked_add(value)2050 .ok_or(Error::<T>::NumOverflow)?;2051 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20522053 let old_owner = item.owner.clone();2054 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20552056 let mut new_full_item = full_item.clone();2057 2058 if amount == value && !new_owner_has_account {2059 2060 2061 new_full_item2062 .owner2063 .iter_mut()2064 .find(|i| i.owner == owner)2065 .expect("old owner does present in refungible")2066 .owner = new_owner.clone();2067 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20682069 2070 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2071 } else {2072 new_full_item2073 .owner2074 .iter_mut()2075 .find(|i| i.owner == owner)2076 .expect("old owner does present in refungible")2077 .fraction -= value;20782079 2080 if new_owner_has_account {2081 2082 new_full_item2083 .owner2084 .iter_mut()2085 .find(|i| i.owner == new_owner)2086 .expect("new owner has account")2087 .fraction += value;2088 } else {2089 2090 new_full_item.owner.push(Ownership {2091 owner: new_owner.clone(),2092 fraction: value,2093 });2094 Self::add_token_index(collection_id, item_id, &new_owner)?;2095 }20962097 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2098 }20992100 Self::deposit_event(RawEvent::Transfer(2101 collection.id,2102 item_id,2103 owner,2104 new_owner,2105 amount,2106 ));21072108 Ok(())2109 }21102111 fn transfer_nft(2112 collection: &CollectionHandle<T>,2113 item_id: TokenId,2114 sender: T::CrossAccountId,2115 new_owner: T::CrossAccountId,2116 ) -> DispatchResult {2117 let collection_id = collection.id;2118 let mut item =2119 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21202121 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21222123 2124 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2125 .checked_sub(1)2126 .ok_or(Error::<T>::NumOverflow)?;2127 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21282129 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2130 .checked_add(1)2131 .ok_or(Error::<T>::NumOverflow)?;2132 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21332134 2135 let old_owner = item.owner.clone();2136 item.owner = new_owner.clone();2137 <NftItemList<T>>::insert(collection_id, item_id, item);21382139 2140 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21412142 collection.log(ERC721Events::Transfer {2143 from: *sender.as_eth(),2144 to: *new_owner.as_eth(),2145 token_id: item_id.into(),2146 })?;2147 Self::deposit_event(RawEvent::Transfer(2148 collection.id,2149 item_id,2150 sender,2151 new_owner,2152 1,2153 ));21542155 Ok(())2156 }21572158 fn set_re_fungible_variable_data(2159 collection: &CollectionHandle<T>,2160 item_id: TokenId,2161 data: Vec<u8>,2162 ) -> DispatchResult {2163 let collection_id = collection.id;2164 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2165 .ok_or(Error::<T>::TokenNotFound)?;21662167 item.variable_data = data;21682169 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21702171 Ok(())2172 }21732174 fn set_nft_variable_data(2175 collection: &CollectionHandle<T>,2176 item_id: TokenId,2177 data: Vec<u8>,2178 ) -> DispatchResult {2179 let collection_id = collection.id;2180 let mut item =2181 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21822183 item.variable_data = data;21842185 <NftItemList<T>>::insert(collection_id, item_id, item);21862187 Ok(())2188 }21892190 #[allow(dead_code)]2191 fn init_collection(item: &Collection<T>) {2192 2193 assert!(2194 item.decimal_points <= MAX_DECIMAL_POINTS,2195 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2196 );2197 assert!(2198 item.name.len() <= 64,2199 "Collection name can not be longer than 63 char"2200 );2201 assert!(2202 item.name.len() <= 256,2203 "Collection description can not be longer than 255 char"2204 );2205 assert!(2206 item.token_prefix.len() <= 16,2207 "Token prefix can not be longer than 15 char"2208 );22092210 2211 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22122213 CreatedCollectionCount::put(next_id);2214 }22152216 #[allow(dead_code)]2217 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2218 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22192220 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22212222 <ItemListIndex>::insert(collection_id, current_index);22232224 2225 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2226 .checked_add(1)2227 .unwrap();2228 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2229 }22302231 #[allow(dead_code)]2232 fn init_fungible_token(2233 collection_id: CollectionId,2234 owner: &T::CrossAccountId,2235 item: &FungibleItemType,2236 ) {2237 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22382239 Self::add_token_index(collection_id, current_index, owner).unwrap();22402241 <ItemListIndex>::insert(collection_id, current_index);22422243 2244 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2245 .checked_add(item.value)2246 .unwrap();2247 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2248 }22492250 #[allow(dead_code)]2251 fn init_refungible_token(2252 collection_id: CollectionId,2253 item: &ReFungibleItemType<T::CrossAccountId>,2254 ) {2255 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22562257 let value = item.owner.first().unwrap().fraction;2258 let owner = item.owner.first().unwrap().owner.clone();22592260 Self::add_token_index(collection_id, current_index, &owner).unwrap();22612262 <ItemListIndex>::insert(collection_id, current_index);22632264 2265 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2266 .checked_add(value)2267 .unwrap();2268 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2269 }22702271 fn add_token_index(2272 collection_id: CollectionId,2273 item_index: TokenId,2274 owner: &T::CrossAccountId,2275 ) -> DispatchResult {2276 2277 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2278 2279 let count = <AccountItemCount<T>>::get(owner.as_sub());2280 ensure!(2281 count < <limit!(T, AccountTokenOwnershipLimit)>::get(),2282 Error::<T>::AddressOwnershipLimitExceeded2283 );22842285 <AccountItemCount<T>>::insert(2286 owner.as_sub(),2287 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2288 );2289 } else {2290 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2291 }22922293 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2294 if list_exists {2295 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2296 let item_contains = list.contains(&item_index.clone());22972298 if !item_contains {2299 list.push(item_index);2300 }23012302 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2303 } else {2304 let itm = vec![item_index];2305 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2306 }23072308 Ok(())2309 }23102311 fn remove_token_index(2312 collection_id: CollectionId,2313 item_index: TokenId,2314 owner: &T::CrossAccountId,2315 ) -> DispatchResult {2316 2317 <AccountItemCount<T>>::insert(2318 owner.as_sub(),2319 <AccountItemCount<T>>::get(owner.as_sub())2320 .checked_sub(1)2321 .ok_or(Error::<T>::NumOverflow)?,2322 );23232324 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2325 if list_exists {2326 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2327 let item_contains = list.contains(&item_index.clone());23282329 if item_contains {2330 list.retain(|&item| item != item_index);2331 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2332 }2333 }23342335 Ok(())2336 }23372338 fn move_token_index(2339 collection_id: CollectionId,2340 item_index: TokenId,2341 old_owner: &T::CrossAccountId,2342 new_owner: &T::CrossAccountId,2343 ) -> DispatchResult {2344 Self::remove_token_index(collection_id, item_index, old_owner)?;2345 Self::add_token_index(collection_id, item_index, new_owner)?;23462347 Ok(())2348 }2349}23502351sp_api::decl_runtime_apis! {2352 pub trait NftApi {2353 2354 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2355 }2356}