123456#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45 FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;6667pub trait WeightInfo {68 fn create_collection() -> Weight;69 fn destroy_collection() -> Weight;70 fn add_to_white_list() -> Weight;71 fn remove_from_white_list() -> Weight;72 fn set_public_access_mode() -> Weight;73 fn set_mint_permission() -> Weight;74 fn change_collection_owner() -> Weight;75 fn add_collection_admin() -> Weight;76 fn remove_collection_admin() -> Weight;77 fn set_collection_sponsor() -> Weight;78 fn confirm_sponsorship() -> Weight;79 fn remove_collection_sponsor() -> Weight;80 fn create_item(s: usize) -> Weight;81 fn burn_item() -> Weight;82 fn transfer() -> Weight;83 fn approve() -> Weight;84 fn transfer_from() -> Weight;85 fn set_offchain_schema() -> Weight;86 fn set_const_on_chain_schema() -> Weight;87 fn set_variable_on_chain_schema() -> Weight;88 fn set_variable_meta_data() -> Weight;89 fn enable_contract_sponsoring() -> Weight;90 fn set_schema_version() -> Weight;91 fn set_chain_limits() -> Weight;92 fn set_contract_sponsoring_rate_limit() -> Weight;93 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;94 fn toggle_contract_white_list() -> Weight;95 fn add_to_contract_white_list() -> Weight;96 fn remove_from_contract_white_list() -> Weight;97 fn set_collection_limits() -> Weight;98}99100decl_error! {101 102 pub enum Error for Module<T: Config> {103 104 TotalCollectionsLimitExceeded,105 106 CollectionDecimalPointLimitExceeded,107 108 CollectionNameLimitExceeded,109 110 CollectionDescriptionLimitExceeded,111 112 CollectionTokenPrefixLimitExceeded,113 114 CollectionNotFound,115 116 TokenNotFound,117 118 AdminNotFound,119 120 NumOverflow,121 122 AlreadyAdmin,123 124 NoPermission,125 126 ConfirmUnsetSponsorFail,127 128 PublicMintingNotAllowed,129 130 MustBeTokenOwner,131 132 TokenValueTooLow,133 134 NftSizeLimitExceeded,135 136 ApproveNotFound,137 138 TokenValueNotEnough,139 140 ApproveRequired,141 142 AddresNotInWhiteList,143 144 CollectionAdminsLimitExceeded,145 146 AddressOwnershipLimitExceeded,147 148 EmptyArgument,149 150 TokenConstDataLimitExceeded,151 152 TokenVariableDataLimitExceeded,153 154 NotNftDataUsedToMintNftCollectionToken,155 156 NotFungibleDataUsedToMintFungibleCollectionToken,157 158 NotReFungibleDataUsedToMintReFungibleCollectionToken,159 160 UnexpectedCollectionType,161 162 CantStoreMetadataInFungibleTokens,163 164 CollectionTokenLimitExceeded,165 166 AccountTokenLimitExceeded,167 168 CollectionLimitBoundsExceeded,169 170 OwnerPermissionsCantBeReverted,171 172 SchemaDataLimitExceeded,173 174 WrongRefungiblePieces,175 176 BadCreateRefungibleCall,177 178 OutOfGas,179 180 TransferNotAllowed,181 }182}183184#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]185pub struct CollectionHandle<T: Config> {186 pub id: CollectionId,187 collection: Collection<T>,188 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,189}190impl<T: Config> CollectionHandle<T> {191 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {192 <CollectionById<T>>::get(id).map(|collection| Self {193 id,194 collection,195 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(196 eth::collection_id_to_address(id),197 gas_limit,198 ),199 })200 }201 pub fn get(id: CollectionId) -> Option<Self> {202 Self::get_with_gas_limit(id, u64::MAX)203 }204 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {205 self.recorder.log_sub(log)206 }207 fn consume_gas(&self, gas: u64) -> DispatchResult {208 self.recorder.consume_gas_sub(gas)209 }210 pub fn submit_logs(self) -> DispatchResult {211 self.recorder.submit_logs()212 }213 pub fn save(self) -> DispatchResult {214 self.recorder.submit_logs()?;215 <CollectionById<T>>::insert(self.id, self.collection);216 Ok(())217 }218}219impl<T: Config> Deref for CollectionHandle<T> {220 type Target = Collection<T>;221222 fn deref(&self) -> &Self::Target {223 &self.collection224 }225}226227impl<T: Config> DerefMut for CollectionHandle<T> {228 fn deref_mut(&mut self) -> &mut Self::Target {229 &mut self.collection230 }231}232233pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {234 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;235236 237 type WeightInfo: WeightInfo;238239 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;240 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;241242 type CrossAccountId: CrossAccountId<Self::AccountId>;243 type Currency: Currency<Self::AccountId>;244 type CollectionCreationPrice: Get<245 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,246 >;247 type TreasuryAccountId: Get<Self::AccountId>;248}249250251252253254255256257258259260261262263264265266267268269270271272decl_storage! {273 trait Store for Module<T: Config> as Nft {274275 276 277 CreatedCollectionCount: u32;278 279 ChainVersion: u64;280 281 282 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;283 284285 286 287 288 DestroyedCollectionCount: u32;289 290 291 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;292 293294 295 296 297 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;298 299 300 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;301 302 303 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;304 305306 307 308 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;309310 311 312 313 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;314315 316 317 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;318 319 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;320 321 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;322 323324 325 326 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;327 328329 330 331 332 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;333 334 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;335 336 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;337 338 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;339 340341 342 343 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;344 }345 add_extra_genesis {346 build(|config: &GenesisConfig<T>| {347 348 for (_num, _c) in &config.collection_id {349 <Module<T>>::init_collection(_c);350 }351352 for (_num, _c, _i) in &config.nft_item_id {353 <Module<T>>::init_nft_token(*_c, _i);354 }355356 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {357 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);358 }359360 for (_num, _c, _i) in &config.refungible_item_id {361 <Module<T>>::init_refungible_token(*_c, _i);362 }363 })364 }365}366367decl_event!(368 pub enum Event<T>369 where370 AccountId = <T as frame_system::Config>::AccountId,371 CrossAccountId = <T as Config>::CrossAccountId,372 {373 374 375 376 377 378 379 380 381 382 CollectionCreated(CollectionId, u8, AccountId),383384 385 386 387 388 389 390 391 392 393 ItemCreated(CollectionId, TokenId, CrossAccountId),394395 396 397 398 399 400 401 402 ItemDestroyed(CollectionId, TokenId),403404 405 406 407 408 409 410 411 412 413 414 415 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),416417 418 419 420 421 422 423 424 425 426 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),427 }428);429430decl_module! {431 pub struct Module<T: Config> for enum Call432 where433 origin: T::Origin434 {435 fn deposit_event() = default;436 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;437 type Error = Error<T>;438439 fn on_initialize(_now: T::BlockNumber) -> Weight {440 0441 }442443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 #[weight = <T as Config>::WeightInfo::create_collection()]460 #[transactional]461 pub fn create_collection(origin,462 collection_name: Vec<u16>,463 collection_description: Vec<u16>,464 token_prefix: Vec<u8>,465 mode: CollectionMode) -> DispatchResult {466467 468 let who = ensure_signed(origin)?;469470 471 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();472 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(473 &T::TreasuryAccountId::get(),474 T::CollectionCreationPrice::get(),475 ));476 <T as Config>::Currency::settle(477 &who,478 imbalance,479 WithdrawReasons::TRANSFER,480 ExistenceRequirement::KeepAlive,481 ).map_err(|_| Error::<T>::NoPermission)?;482483 let decimal_points = match mode {484 CollectionMode::Fungible(points) => points,485 _ => 0486 };487488 let created_count = CreatedCollectionCount::get();489 let destroyed_count = DestroyedCollectionCount::get();490491 492 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);493494 495 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);496 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);497 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);498 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);499500 501 let next_id = created_count502 .checked_add(1)503 .ok_or(Error::<T>::NumOverflow)?;504505 CreatedCollectionCount::put(next_id);506507 let limits = CollectionLimits {508 sponsored_data_size: CUSTOM_DATA_LIMIT,509 ..Default::default()510 };511512 513 let new_collection = Collection {514 owner: who.clone(),515 name: collection_name,516 mode: mode.clone(),517 mint_mode: false,518 access: AccessMode::Normal,519 description: collection_description,520 decimal_points,521 token_prefix,522 offchain_schema: Vec::new(),523 schema_version: SchemaVersion::ImageURL,524 sponsorship: SponsorshipState::Disabled,525 variable_on_chain_schema: Vec::new(),526 const_on_chain_schema: Vec::new(),527 limits,528 transfers_enabled: true,529 };530531 532 <CollectionById<T>>::insert(next_id, new_collection);533534 535 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));536537 Ok(())538 }539540 541 542 543 544 545 546 547 548 549 #[weight = <T as Config>::WeightInfo::destroy_collection()]550 #[transactional]551 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {552553 let sender = ensure_signed(origin)?;554 let collection = Self::get_collection(collection_id)?;555 Self::check_owner_permissions(&collection, &sender)?;556 if !collection.limits.owner_can_destroy {557 fail!(Error::<T>::NoPermission);558 }559560 <AddressTokens<T>>::remove_prefix(collection_id, None);561 <Allowances<T>>::remove_prefix(collection_id, None);562 <Balance<T>>::remove_prefix(collection_id, None);563 <ItemListIndex>::remove(collection_id);564 <AdminList<T>>::remove(collection_id);565 <CollectionById<T>>::remove(collection_id);566 <WhiteList<T>>::remove_prefix(collection_id, None);567568 <NftItemList<T>>::remove_prefix(collection_id, None);569 <FungibleItemList<T>>::remove_prefix(collection_id, None);570 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);571572 <NftTransferBasket<T>>::remove_prefix(collection_id, None);573 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);574 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);575576 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);577578 DestroyedCollectionCount::put(DestroyedCollectionCount::get()579 .checked_add(1)580 .ok_or(Error::<T>::NumOverflow)?);581582 Ok(())583 }584585 586 587 588 589 590 591 592 593 594 595 596 597 #[weight = <T as Config>::WeightInfo::add_to_white_list()]598 #[transactional]599 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{600601 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);602 let collection = Self::get_collection(collection_id)?;603604 Self::toggle_white_list_internal(605 &sender,606 &collection,607 &address,608 true,609 )?;610611 Ok(())612 }613614 615 616 617 618 619 620 621 622 623 624 625 626 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]627 #[transactional]628 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{629630 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);631 let collection = Self::get_collection(collection_id)?;632633 Self::toggle_white_list_internal(634 &sender,635 &collection,636 &address,637 false,638 )?;639640 Ok(())641 }642643 644 645 646 647 648 649 650 651 652 653 654 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]655 #[transactional]656 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult657 {658 let sender = ensure_signed(origin)?;659660 let mut target_collection = Self::get_collection(collection_id)?;661 Self::check_owner_permissions(&target_collection, &sender)?;662 target_collection.access = mode;663 target_collection.save()664 }665666 667 668 669 670 671 672 673 674 675 676 677 678 679 #[weight = <T as Config>::WeightInfo::set_mint_permission()]680 #[transactional]681 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult682 {683 let sender = ensure_signed(origin)?;684685 let mut target_collection = Self::get_collection(collection_id)?;686 Self::check_owner_permissions(&target_collection, &sender)?;687 target_collection.mint_mode = mint_permission;688 target_collection.save()689 }690691 692 693 694 695 696 697 698 699 700 701 702 #[weight = <T as Config>::WeightInfo::change_collection_owner()]703 #[transactional]704 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {705706 let sender = ensure_signed(origin)?;707 let mut target_collection = Self::get_collection(collection_id)?;708 Self::check_owner_permissions(&target_collection, &sender)?;709 target_collection.owner = new_owner;710 target_collection.save()711 }712713 714 715 716 717 718 719 720 721 722 723 724 725 726 #[weight = <T as Config>::WeightInfo::add_collection_admin()]727 #[transactional]728 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {729 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);730 let collection = Self::get_collection(collection_id)?;731 Self::check_owner_or_admin_permissions(&collection, &sender)?;732 let mut admin_arr = <AdminList<T>>::get(collection_id);733734 match admin_arr.binary_search(&new_admin_id) {735 Ok(_) => {},736 Err(idx) => {737 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);738 admin_arr.insert(idx, new_admin_id);739 <AdminList<T>>::insert(collection_id, admin_arr);740 }741 }742 Ok(())743 }744745 746 747 748 749 750 751 752 753 754 755 756 757 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]758 #[transactional]759 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {760 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);761 let collection = Self::get_collection(collection_id)?;762 Self::check_owner_or_admin_permissions(&collection, &sender)?;763 let mut admin_arr = <AdminList<T>>::get(collection_id);764765 if let Ok(idx) = admin_arr.binary_search(&account_id) {766 admin_arr.remove(idx);767 <AdminList<T>>::insert(collection_id, admin_arr);768 }769 Ok(())770 }771772 773 774 775 776 777 778 779 780 781 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]782 #[transactional]783 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {784 let sender = ensure_signed(origin)?;785 let mut target_collection = Self::get_collection(collection_id)?;786 Self::check_owner_permissions(&target_collection, &sender)?;787788 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);789 target_collection.save()790 }791792 793 794 795 796 797 798 799 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]800 #[transactional]801 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {802 let sender = ensure_signed(origin)?;803804 let mut target_collection = Self::get_collection(collection_id)?;805 ensure!(806 target_collection.sponsorship.pending_sponsor() == Some(&sender),807 Error::<T>::ConfirmUnsetSponsorFail808 );809810 target_collection.sponsorship = SponsorshipState::Confirmed(sender);811 target_collection.save()812 }813814 815 816 817 818 819 820 821 822 823 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]824 #[transactional]825 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {826 let sender = ensure_signed(origin)?;827828 let mut target_collection = Self::get_collection(collection_id)?;829 Self::check_owner_permissions(&target_collection, &sender)?;830831 target_collection.sponsorship = SponsorshipState::Disabled;832 target_collection.save()833 }834835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858859 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]860 #[transactional]861 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {862 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);863 let collection = Self::get_collection(collection_id)?;864865 Self::create_item_internal(&sender, &collection, &owner, data)?;866867 collection.submit_logs()868 }869870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()889 .map(|data| { data.data_size() })890 .sum())]891 #[transactional]892 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {893894 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897898 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;899900 collection.submit_logs()901 }902903 904905 906 907 908 909 910 911 912 913 914 915 916 #[weight = <T as Config>::WeightInfo::burn_item()]917 #[transactional]918 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {919920 let sender = ensure_signed(origin)?;921 let mut target_collection = Self::get_collection(collection_id)?;922923 Self::check_owner_permissions(&target_collection, &sender)?;924925 target_collection.transfers_enabled = value;926 target_collection.save()927 }928929 930 931 932 933 934 935 936 937 938 939 940 941 942 #[weight = <T as Config>::WeightInfo::burn_item()]943 #[transactional]944 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {945946 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);947 let target_collection = Self::get_collection(collection_id)?;948949 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;950951 target_collection.submit_logs()952 }953954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 #[weight = <T as Config>::WeightInfo::transfer()]978 #[transactional]979 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {980 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981 let collection = Self::get_collection(collection_id)?;982983 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;984985 collection.submit_logs()986 }987988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 #[weight = <T as Config>::WeightInfo::approve()]1004 #[transactional]1005 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1006 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1007 let collection = Self::get_collection(collection_id)?;10081009 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10101011 collection.submit_logs()1012 }10131014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 #[weight = <T as Config>::WeightInfo::transfer_from()]1034 #[transactional]1035 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1036 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1037 let collection = Self::get_collection(collection_id)?;10381039 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10401041 collection.submit_logs()1042 }1043 1044 1045 1046 1047 10481049 10501051 10521053 1054 10551056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1069 #[transactional]1070 pub fn set_variable_meta_data (1071 origin,1072 collection_id: CollectionId,1073 item_id: TokenId,1074 data: Vec<u8>1075 ) -> DispatchResult {1076 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10771078 let collection = Self::get_collection(collection_id)?;10791080 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10811082 Ok(())1083 }10841085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 #[weight = <T as Config>::WeightInfo::set_schema_version()]1100 #[transactional]1101 pub fn set_schema_version(1102 origin,1103 collection_id: CollectionId,1104 version: SchemaVersion1105 ) -> DispatchResult {1106 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1107 let mut target_collection = Self::get_collection(collection_id)?;1108 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1109 target_collection.schema_version = version;1110 target_collection.save()1111 }11121113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1126 #[transactional]1127 pub fn set_offchain_schema(1128 origin,1129 collection_id: CollectionId,1130 schema: Vec<u8>1131 ) -> DispatchResult {1132 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1133 let mut target_collection = Self::get_collection(collection_id)?;1134 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11351136 1137 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11381139 target_collection.offchain_schema = schema;1140 target_collection.save()1141 }11421143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1156 #[transactional]1157 pub fn set_const_on_chain_schema (1158 origin,1159 collection_id: CollectionId,1160 schema: Vec<u8>1161 ) -> DispatchResult {1162 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1163 let mut target_collection = Self::get_collection(collection_id)?;1164 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11651166 1167 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11681169 target_collection.const_on_chain_schema = schema;1170 target_collection.save()1171 }11721173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1186 #[transactional]1187 pub fn set_variable_on_chain_schema (1188 origin,1189 collection_id: CollectionId,1190 schema: Vec<u8>1191 ) -> DispatchResult {1192 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1193 let mut target_collection = Self::get_collection(collection_id)?;1194 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11951196 1197 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11981199 target_collection.variable_on_chain_schema = schema;1200 target_collection.save()1201 }12021203 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1204 #[transactional]1205 pub fn set_collection_limits(1206 origin,1207 collection_id: u32,1208 new_limits: CollectionLimits<T::BlockNumber>,1209 ) -> DispatchResult {1210 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1211 let mut target_collection = Self::get_collection(collection_id)?;1212 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1213 let old_limits = &target_collection.limits;12141215 1216 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1217 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1218 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1219 Error::<T>::CollectionLimitBoundsExceeded);12201221 1222 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1223 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12241225 ensure!(1226 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1227 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1228 Error::<T>::OwnerPermissionsCantBeReverted,1229 );12301231 target_collection.limits = new_limits;12321233 target_collection.save()1234 }1235 }1236}12371238impl<T: Config> Module<T> {1239 pub fn create_item_internal(1240 sender: &T::CrossAccountId,1241 collection: &CollectionHandle<T>,1242 owner: &T::CrossAccountId,1243 data: CreateItemData,1244 ) -> DispatchResult {1245 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1246 Self::validate_create_item_args(collection, &data)?;1247 Self::create_item_no_validation(collection, owner, data)?;12481249 Ok(())1250 }12511252 pub fn transfer_internal(1253 sender: &T::CrossAccountId,1254 recipient: &T::CrossAccountId,1255 target_collection: &CollectionHandle<T>,1256 item_id: TokenId,1257 value: u128,1258 ) -> DispatchResult {1259 target_collection.consume_gas(2000000)?;1260 1261 Self::is_correct_transfer(target_collection, recipient)?;12621263 1264 ensure!(1265 Self::is_item_owner(sender, target_collection, item_id)1266 || Self::is_owner_or_admin_permissions(target_collection, sender),1267 Error::<T>::NoPermission1268 );12691270 if target_collection.access == AccessMode::WhiteList {1271 Self::check_white_list(target_collection, sender)?;1272 Self::check_white_list(target_collection, recipient)?;1273 }12741275 match target_collection.mode {1276 CollectionMode::NFT => Self::transfer_nft(1277 target_collection,1278 item_id,1279 sender.clone(),1280 recipient.clone(),1281 )?,1282 CollectionMode::Fungible(_) => {1283 Self::transfer_fungible(target_collection, value, sender, recipient)?1284 }1285 CollectionMode::ReFungible => Self::transfer_refungible(1286 target_collection,1287 item_id,1288 value,1289 sender.clone(),1290 recipient.clone(),1291 )?,1292 _ => (),1293 };12941295 Self::deposit_event(RawEvent::Transfer(1296 target_collection.id,1297 item_id,1298 sender.clone(),1299 recipient.clone(),1300 value,1301 ));13021303 Ok(())1304 }13051306 pub fn approve_internal(1307 sender: &T::CrossAccountId,1308 spender: &T::CrossAccountId,1309 collection: &CollectionHandle<T>,1310 item_id: TokenId,1311 amount: u128,1312 ) -> DispatchResult {1313 collection.consume_gas(2000000)?;1314 Self::token_exists(collection, item_id)?;13151316 1317 let bypasses_limits = collection.limits.owner_can_transfer1318 && Self::is_owner_or_admin_permissions(collection, sender);13191320 let allowance_limit = if bypasses_limits {1321 None1322 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1323 Some(amount)1324 } else {1325 fail!(Error::<T>::NoPermission);1326 };13271328 if collection.access == AccessMode::WhiteList {1329 Self::check_white_list(collection, sender)?;1330 Self::check_white_list(collection, spender)?;1331 }13321333 let allowance: u128 = amount1334 .checked_add(<Allowances<T>>::get(1335 collection.id,1336 (item_id, sender.as_sub(), spender.as_sub()),1337 ))1338 .ok_or(Error::<T>::NumOverflow)?;1339 if let Some(limit) = allowance_limit {1340 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1341 }1342 <Allowances<T>>::insert(1343 collection.id,1344 (item_id, sender.as_sub(), spender.as_sub()),1345 allowance,1346 );13471348 if matches!(collection.mode, CollectionMode::NFT) {1349 1350 collection.log(ERC721Events::Approval {1351 owner: *sender.as_eth(),1352 approved: *spender.as_eth(),1353 token_id: item_id.into(),1354 })?;1355 }13561357 if matches!(collection.mode, CollectionMode::Fungible(_)) {1358 1359 collection.log(ERC20Events::Approval {1360 owner: *sender.as_eth(),1361 spender: *spender.as_eth(),1362 value: allowance.into(),1363 })?;1364 }13651366 Self::deposit_event(RawEvent::Approved(1367 collection.id,1368 item_id,1369 sender.clone(),1370 spender.clone(),1371 allowance,1372 ));1373 Ok(())1374 }13751376 pub fn transfer_from_internal(1377 sender: &T::CrossAccountId,1378 from: &T::CrossAccountId,1379 recipient: &T::CrossAccountId,1380 collection: &CollectionHandle<T>,1381 item_id: TokenId,1382 amount: u128,1383 ) -> DispatchResult {1384 collection.consume_gas(2000000)?;1385 1386 let approval: u128 =1387 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13881389 1390 Self::is_correct_transfer(collection, recipient)?;13911392 1393 ensure!(1394 approval >= amount1395 || (collection.limits.owner_can_transfer1396 && Self::is_owner_or_admin_permissions(collection, sender)),1397 Error::<T>::NoPermission1398 );13991400 if collection.access == AccessMode::WhiteList {1401 Self::check_white_list(collection, sender)?;1402 Self::check_white_list(collection, recipient)?;1403 }14041405 1406 let allowance = approval.saturating_sub(amount);1407 if allowance > 0 {1408 <Allowances<T>>::insert(1409 collection.id,1410 (item_id, from.as_sub(), sender.as_sub()),1411 allowance,1412 );1413 } else {1414 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1415 }14161417 match collection.mode {1418 CollectionMode::NFT => {1419 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1420 }1421 CollectionMode::Fungible(_) => {1422 Self::transfer_fungible(collection, amount, from, recipient)?1423 }1424 CollectionMode::ReFungible => Self::transfer_refungible(1425 collection,1426 item_id,1427 amount,1428 from.clone(),1429 recipient.clone(),1430 )?,1431 _ => (),1432 };14331434 if matches!(collection.mode, CollectionMode::Fungible(_)) {1435 collection.log(ERC20Events::Approval {1436 owner: *from.as_eth(),1437 spender: *sender.as_eth(),1438 value: allowance.into(),1439 })?;1440 }14411442 Ok(())1443 }14441445 pub fn set_variable_meta_data_internal(1446 sender: &T::CrossAccountId,1447 collection: &CollectionHandle<T>,1448 item_id: TokenId,1449 data: Vec<u8>,1450 ) -> DispatchResult {1451 Self::token_exists(collection, item_id)?;14521453 ensure!(1454 CUSTOM_DATA_LIMIT >= data.len() as u32,1455 Error::<T>::TokenVariableDataLimitExceeded1456 );14571458 1459 ensure!(1460 Self::is_item_owner(sender, collection, item_id)1461 || Self::is_owner_or_admin_permissions(collection, sender),1462 Error::<T>::NoPermission1463 );14641465 match collection.mode {1466 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1467 CollectionMode::ReFungible => {1468 Self::set_re_fungible_variable_data(collection, item_id, data)?1469 }1470 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1471 _ => fail!(Error::<T>::UnexpectedCollectionType),1472 };14731474 Ok(())1475 }14761477 pub fn create_multiple_items_internal(1478 sender: &T::CrossAccountId,1479 collection: &CollectionHandle<T>,1480 owner: &T::CrossAccountId,1481 items_data: Vec<CreateItemData>,1482 ) -> DispatchResult {1483 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14841485 for data in &items_data {1486 Self::validate_create_item_args(collection, data)?;1487 }1488 for data in &items_data {1489 Self::create_item_no_validation(collection, owner, data.clone())?;1490 }14911492 Ok(())1493 }14941495 pub fn burn_item_internal(1496 sender: &T::CrossAccountId,1497 collection: &CollectionHandle<T>,1498 item_id: TokenId,1499 value: u128,1500 ) -> DispatchResult {1501 ensure!(1502 Self::is_item_owner(sender, collection, item_id)1503 || (collection.limits.owner_can_transfer1504 && Self::is_owner_or_admin_permissions(collection, sender)),1505 Error::<T>::NoPermission1506 );15071508 if collection.access == AccessMode::WhiteList {1509 Self::check_white_list(collection, sender)?;1510 }15111512 match collection.mode {1513 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1514 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1515 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1516 _ => (),1517 };15181519 Ok(())1520 }15211522 pub fn toggle_white_list_internal(1523 sender: &T::CrossAccountId,1524 collection: &CollectionHandle<T>,1525 address: &T::CrossAccountId,1526 whitelisted: bool,1527 ) -> DispatchResult {1528 Self::check_owner_or_admin_permissions(collection, sender)?;15291530 if whitelisted {1531 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1532 } else {1533 <WhiteList<T>>::remove(collection.id, address.as_sub());1534 }15351536 Ok(())1537 }15381539 fn is_correct_transfer(1540 collection: &CollectionHandle<T>,1541 recipient: &T::CrossAccountId,1542 ) -> DispatchResult {1543 let collection_id = collection.id;15441545 1546 let account_items: u32 =1547 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1548 ensure!(1549 collection.limits.account_token_ownership_limit > account_items,1550 Error::<T>::AccountTokenLimitExceeded1551 );15521553 1554 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15551556 Ok(())1557 }15581559 fn can_create_items_in_collection(1560 collection: &CollectionHandle<T>,1561 sender: &T::CrossAccountId,1562 owner: &T::CrossAccountId,1563 amount: u32,1564 ) -> DispatchResult {1565 let collection_id = collection.id;15661567 1568 let total_items: u32 = ItemListIndex::get(collection_id)1569 .checked_add(amount)1570 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1571 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1572 as u32)1573 .checked_add(amount)1574 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1575 ensure!(1576 collection.limits.token_limit >= total_items,1577 Error::<T>::CollectionTokenLimitExceeded1578 );1579 ensure!(1580 collection.limits.account_token_ownership_limit >= account_items,1581 Error::<T>::AccountTokenLimitExceeded1582 );15831584 if !Self::is_owner_or_admin_permissions(collection, sender) {1585 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1586 Self::check_white_list(collection, owner)?;1587 Self::check_white_list(collection, sender)?;1588 }15891590 Ok(())1591 }15921593 fn validate_create_item_args(1594 target_collection: &CollectionHandle<T>,1595 data: &CreateItemData,1596 ) -> DispatchResult {1597 match target_collection.mode {1598 CollectionMode::NFT => {1599 if let CreateItemData::NFT(data) = data {1600 1601 ensure!(1602 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1603 Error::<T>::TokenConstDataLimitExceeded1604 );1605 ensure!(1606 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1607 Error::<T>::TokenVariableDataLimitExceeded1608 );1609 } else {1610 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1611 }1612 }1613 CollectionMode::Fungible(_) => {1614 if let CreateItemData::Fungible(_) = data {1615 } else {1616 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1617 }1618 }1619 CollectionMode::ReFungible => {1620 if let CreateItemData::ReFungible(data) = data {1621 1622 ensure!(1623 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1624 Error::<T>::TokenConstDataLimitExceeded1625 );1626 ensure!(1627 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1628 Error::<T>::TokenVariableDataLimitExceeded1629 );16301631 1632 ensure!(1633 data.pieces <= MAX_REFUNGIBLE_PIECES,1634 Error::<T>::WrongRefungiblePieces1635 );1636 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1637 } else {1638 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1639 }1640 }1641 _ => {1642 fail!(Error::<T>::UnexpectedCollectionType);1643 }1644 };16451646 Ok(())1647 }16481649 fn create_item_no_validation(1650 collection: &CollectionHandle<T>,1651 owner: &T::CrossAccountId,1652 data: CreateItemData,1653 ) -> DispatchResult {1654 match data {1655 CreateItemData::NFT(data) => {1656 let item = NftItemType {1657 owner: owner.clone(),1658 const_data: data.const_data.into_inner(),1659 variable_data: data.variable_data.into_inner(),1660 };16611662 Self::add_nft_item(collection, item)?;1663 }1664 CreateItemData::Fungible(data) => {1665 Self::add_fungible_item(collection, owner, data.value)?;1666 }1667 CreateItemData::ReFungible(data) => {1668 let owner_list = vec![Ownership {1669 owner: owner.clone(),1670 fraction: data.pieces,1671 }];16721673 let item = ReFungibleItemType {1674 owner: owner_list,1675 const_data: data.const_data.into_inner(),1676 variable_data: data.variable_data.into_inner(),1677 };16781679 Self::add_refungible_item(collection, item)?;1680 }1681 };16821683 Ok(())1684 }16851686 fn add_fungible_item(1687 collection: &CollectionHandle<T>,1688 owner: &T::CrossAccountId,1689 value: u128,1690 ) -> DispatchResult {1691 let collection_id = collection.id;16921693 1694 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16951696 1697 let item = FungibleItemType {1698 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1699 };1700 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17011702 1703 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1704 .checked_add(value)1705 .ok_or(Error::<T>::NumOverflow)?;1706 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17071708 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1709 Ok(())1710 }17111712 fn add_refungible_item(1713 collection: &CollectionHandle<T>,1714 item: ReFungibleItemType<T::CrossAccountId>,1715 ) -> DispatchResult {1716 let collection_id = collection.id;17171718 let current_index = <ItemListIndex>::get(collection_id)1719 .checked_add(1)1720 .ok_or(Error::<T>::NumOverflow)?;1721 let itemcopy = item.clone();17221723 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1724 let item_owner = item.owner.first().expect("only one owner is defined");17251726 let value = item_owner.fraction;1727 let owner = item_owner.owner.clone();17281729 Self::add_token_index(collection_id, current_index, &owner)?;17301731 <ItemListIndex>::insert(collection_id, current_index);1732 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17331734 1735 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1736 .checked_add(value)1737 .ok_or(Error::<T>::NumOverflow)?;1738 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17391740 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1741 Ok(())1742 }17431744 fn add_nft_item(1745 collection: &CollectionHandle<T>,1746 item: NftItemType<T::CrossAccountId>,1747 ) -> DispatchResult {1748 let collection_id = collection.id;17491750 let current_index = <ItemListIndex>::get(collection_id)1751 .checked_add(1)1752 .ok_or(Error::<T>::NumOverflow)?;17531754 let item_owner = item.owner.clone();1755 Self::add_token_index(collection_id, current_index, &item.owner)?;17561757 <ItemListIndex>::insert(collection_id, current_index);1758 <NftItemList<T>>::insert(collection_id, current_index, item);17591760 1761 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1762 .checked_add(1)1763 .ok_or(Error::<T>::NumOverflow)?;1764 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17651766 collection.log(ERC721Events::Transfer {1767 from: H160::default(),1768 to: *item_owner.as_eth(),1769 token_id: current_index.into(),1770 })?;1771 Self::deposit_event(RawEvent::ItemCreated(1772 collection_id,1773 current_index,1774 item_owner,1775 ));1776 Ok(())1777 }17781779 fn burn_refungible_item(1780 collection: &CollectionHandle<T>,1781 item_id: TokenId,1782 owner: &T::CrossAccountId,1783 ) -> DispatchResult {1784 let collection_id = collection.id;17851786 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1787 .ok_or(Error::<T>::TokenNotFound)?;1788 let rft_balance = token1789 .owner1790 .iter()1791 .find(|&i| i.owner == *owner)1792 .ok_or(Error::<T>::TokenNotFound)?;1793 Self::remove_token_index(collection_id, item_id, owner)?;17941795 1796 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1797 .checked_sub(rft_balance.fraction)1798 .ok_or(Error::<T>::NumOverflow)?;1799 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18001801 1802 let index = token1803 .owner1804 .iter()1805 .position(|i| i.owner == *owner)1806 .expect("owned item is exists");1807 token.owner.remove(index);1808 let owner_count = token.owner.len();18091810 1811 if owner_count == 0 {1812 <ReFungibleItemList<T>>::remove(collection_id, item_id);1813 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1814 } else {1815 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1816 }18171818 Ok(())1819 }18201821 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1822 let collection_id = collection.id;18231824 let item =1825 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1826 Self::remove_token_index(collection_id, item_id, &item.owner)?;18271828 1829 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1830 .checked_sub(1)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1833 <NftItemList<T>>::remove(collection_id, item_id);1834 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18351836 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1837 Ok(())1838 }18391840 fn burn_fungible_item(1841 owner: &T::CrossAccountId,1842 collection: &CollectionHandle<T>,1843 value: u128,1844 ) -> DispatchResult {1845 let collection_id = collection.id;18461847 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1848 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18491850 1851 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1852 .checked_sub(value)1853 .ok_or(Error::<T>::NumOverflow)?;1854 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18551856 if balance.value - value > 0 {1857 balance.value -= value;1858 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1859 } else {1860 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1861 }18621863 collection.log(ERC20Events::Transfer {1864 from: *owner.as_eth(),1865 to: H160::default(),1866 value: value.into(),1867 })?;1868 Ok(())1869 }18701871 pub fn get_collection(1872 collection_id: CollectionId,1873 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1874 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1875 }18761877 fn check_owner_permissions(1878 target_collection: &CollectionHandle<T>,1879 subject: &T::AccountId,1880 ) -> DispatchResult {1881 ensure!(1882 *subject == target_collection.owner,1883 Error::<T>::NoPermission1884 );18851886 Ok(())1887 }18881889 fn is_owner_or_admin_permissions(1890 collection: &CollectionHandle<T>,1891 subject: &T::CrossAccountId,1892 ) -> bool {1893 *subject.as_sub() == collection.owner1894 || <AdminList<T>>::get(collection.id).contains(subject)1895 }18961897 fn check_owner_or_admin_permissions(1898 collection: &CollectionHandle<T>,1899 subject: &T::CrossAccountId,1900 ) -> DispatchResult {1901 ensure!(1902 Self::is_owner_or_admin_permissions(collection, subject),1903 Error::<T>::NoPermission1904 );19051906 Ok(())1907 }19081909 fn owned_amount(1910 subject: &T::CrossAccountId,1911 target_collection: &CollectionHandle<T>,1912 item_id: TokenId,1913 ) -> Option<u128> {1914 let collection_id = target_collection.id;19151916 match target_collection.mode {1917 CollectionMode::NFT => {1918 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1919 }1920 CollectionMode::Fungible(_) => {1921 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1922 }1923 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1924 .owner1925 .iter()1926 .find(|i| i.owner == *subject)1927 .map(|i| i.fraction),1928 CollectionMode::Invalid => None,1929 }1930 }19311932 fn is_item_owner(1933 subject: &T::CrossAccountId,1934 target_collection: &CollectionHandle<T>,1935 item_id: TokenId,1936 ) -> bool {1937 match target_collection.mode {1938 CollectionMode::Fungible(_) => true,1939 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1940 }1941 }19421943 fn check_white_list(1944 collection: &CollectionHandle<T>,1945 address: &T::CrossAccountId,1946 ) -> DispatchResult {1947 let collection_id = collection.id;19481949 let mes = Error::<T>::AddresNotInWhiteList;1950 ensure!(1951 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1952 mes1953 );19541955 Ok(())1956 }19571958 1959 1960 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1961 let collection_id = target_collection.id;1962 let exists = match target_collection.mode {1963 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1964 CollectionMode::Fungible(_) => true,1965 CollectionMode::ReFungible => {1966 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1967 }1968 _ => false,1969 };19701971 ensure!(exists, Error::<T>::TokenNotFound);1972 Ok(())1973 }19741975 fn transfer_fungible(1976 collection: &CollectionHandle<T>,1977 value: u128,1978 owner: &T::CrossAccountId,1979 recipient: &T::CrossAccountId,1980 ) -> DispatchResult {1981 let collection_id = collection.id;19821983 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1984 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19851986 1987 Self::add_fungible_item(collection, recipient, value)?;19881989 1990 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19911992 1993 if balance.value == value {1994 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1995 } else {1996 balance.value -= value;1997 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1998 }19992000 collection.log(ERC20Events::Transfer {2001 from: *owner.as_eth(),2002 to: *recipient.as_eth(),2003 value: value.into(),2004 })?;2005 Self::deposit_event(RawEvent::Transfer(2006 collection.id,2007 1,2008 owner.clone(),2009 recipient.clone(),2010 value,2011 ));20122013 Ok(())2014 }20152016 fn transfer_refungible(2017 collection: &CollectionHandle<T>,2018 item_id: TokenId,2019 value: u128,2020 owner: T::CrossAccountId,2021 new_owner: T::CrossAccountId,2022 ) -> DispatchResult {2023 let collection_id = collection.id;2024 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2025 .ok_or(Error::<T>::TokenNotFound)?;20262027 let item = full_item2028 .owner2029 .iter()2030 .find(|i| i.owner == owner)2031 .ok_or(Error::<T>::TokenNotFound)?;2032 let amount = item.fraction;20332034 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20352036 2037 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2038 .checked_sub(value)2039 .ok_or(Error::<T>::NumOverflow)?;2040 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20412042 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2043 .checked_add(value)2044 .ok_or(Error::<T>::NumOverflow)?;2045 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20462047 let old_owner = item.owner.clone();2048 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20492050 let mut new_full_item = full_item.clone();2051 2052 if amount == value && !new_owner_has_account {2053 2054 2055 new_full_item2056 .owner2057 .iter_mut()2058 .find(|i| i.owner == owner)2059 .expect("old owner does present in refungible")2060 .owner = new_owner.clone();2061 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20622063 2064 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2065 } else {2066 new_full_item2067 .owner2068 .iter_mut()2069 .find(|i| i.owner == owner)2070 .expect("old owner does present in refungible")2071 .fraction -= value;20722073 2074 if new_owner_has_account {2075 2076 new_full_item2077 .owner2078 .iter_mut()2079 .find(|i| i.owner == new_owner)2080 .expect("new owner has account")2081 .fraction += value;2082 } else {2083 2084 new_full_item.owner.push(Ownership {2085 owner: new_owner.clone(),2086 fraction: value,2087 });2088 Self::add_token_index(collection_id, item_id, &new_owner)?;2089 }20902091 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2092 }20932094 Self::deposit_event(RawEvent::Transfer(2095 collection.id,2096 item_id,2097 owner,2098 new_owner,2099 amount,2100 ));21012102 Ok(())2103 }21042105 fn transfer_nft(2106 collection: &CollectionHandle<T>,2107 item_id: TokenId,2108 sender: T::CrossAccountId,2109 new_owner: T::CrossAccountId,2110 ) -> DispatchResult {2111 let collection_id = collection.id;2112 let mut item =2113 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21142115 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21162117 2118 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2119 .checked_sub(1)2120 .ok_or(Error::<T>::NumOverflow)?;2121 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21222123 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2124 .checked_add(1)2125 .ok_or(Error::<T>::NumOverflow)?;2126 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21272128 2129 let old_owner = item.owner.clone();2130 item.owner = new_owner.clone();2131 <NftItemList<T>>::insert(collection_id, item_id, item);21322133 2134 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21352136 collection.log(ERC721Events::Transfer {2137 from: *sender.as_eth(),2138 to: *new_owner.as_eth(),2139 token_id: item_id.into(),2140 })?;2141 Self::deposit_event(RawEvent::Transfer(2142 collection.id,2143 item_id,2144 sender,2145 new_owner,2146 1,2147 ));21482149 Ok(())2150 }21512152 fn set_re_fungible_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 = <ReFungibleItemList<T>>::get(collection_id, item_id)2159 .ok_or(Error::<T>::TokenNotFound)?;21602161 item.variable_data = data;21622163 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21642165 Ok(())2166 }21672168 fn set_nft_variable_data(2169 collection: &CollectionHandle<T>,2170 item_id: TokenId,2171 data: Vec<u8>,2172 ) -> DispatchResult {2173 let collection_id = collection.id;2174 let mut item =2175 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21762177 item.variable_data = data;21782179 <NftItemList<T>>::insert(collection_id, item_id, item);21802181 Ok(())2182 }21832184 #[allow(dead_code)]2185 fn init_collection(item: &Collection<T>) {2186 2187 assert!(2188 item.decimal_points <= MAX_DECIMAL_POINTS,2189 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2190 );2191 assert!(2192 item.name.len() <= 64,2193 "Collection name can not be longer than 63 char"2194 );2195 assert!(2196 item.name.len() <= 256,2197 "Collection description can not be longer than 255 char"2198 );2199 assert!(2200 item.token_prefix.len() <= 16,2201 "Token prefix can not be longer than 15 char"2202 );22032204 2205 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22062207 CreatedCollectionCount::put(next_id);2208 }22092210 #[allow(dead_code)]2211 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2212 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22132214 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22152216 <ItemListIndex>::insert(collection_id, current_index);22172218 2219 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2220 .checked_add(1)2221 .unwrap();2222 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2223 }22242225 #[allow(dead_code)]2226 fn init_fungible_token(2227 collection_id: CollectionId,2228 owner: &T::CrossAccountId,2229 item: &FungibleItemType,2230 ) {2231 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22322233 Self::add_token_index(collection_id, current_index, owner).unwrap();22342235 <ItemListIndex>::insert(collection_id, current_index);22362237 2238 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2239 .checked_add(item.value)2240 .unwrap();2241 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2242 }22432244 #[allow(dead_code)]2245 fn init_refungible_token(2246 collection_id: CollectionId,2247 item: &ReFungibleItemType<T::CrossAccountId>,2248 ) {2249 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22502251 let value = item.owner.first().unwrap().fraction;2252 let owner = item.owner.first().unwrap().owner.clone();22532254 Self::add_token_index(collection_id, current_index, &owner).unwrap();22552256 <ItemListIndex>::insert(collection_id, current_index);22572258 2259 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2260 .checked_add(value)2261 .unwrap();2262 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2263 }22642265 fn add_token_index(2266 collection_id: CollectionId,2267 item_index: TokenId,2268 owner: &T::CrossAccountId,2269 ) -> DispatchResult {2270 2271 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2272 2273 let count = <AccountItemCount<T>>::get(owner.as_sub());2274 ensure!(2275 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2276 Error::<T>::AddressOwnershipLimitExceeded2277 );22782279 <AccountItemCount<T>>::insert(2280 owner.as_sub(),2281 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2282 );2283 } else {2284 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2285 }22862287 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2288 if list_exists {2289 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2290 let item_contains = list.contains(&item_index.clone());22912292 if !item_contains {2293 list.push(item_index);2294 }22952296 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2297 } else {2298 let itm = vec![item_index];2299 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2300 }23012302 Ok(())2303 }23042305 fn remove_token_index(2306 collection_id: CollectionId,2307 item_index: TokenId,2308 owner: &T::CrossAccountId,2309 ) -> DispatchResult {2310 2311 <AccountItemCount<T>>::insert(2312 owner.as_sub(),2313 <AccountItemCount<T>>::get(owner.as_sub())2314 .checked_sub(1)2315 .ok_or(Error::<T>::NumOverflow)?,2316 );23172318 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2319 if list_exists {2320 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2321 let item_contains = list.contains(&item_index.clone());23222323 if item_contains {2324 list.retain(|&item| item != item_index);2325 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2326 }2327 }23282329 Ok(())2330 }23312332 fn move_token_index(2333 collection_id: CollectionId,2334 item_index: TokenId,2335 old_owner: &T::CrossAccountId,2336 new_owner: &T::CrossAccountId,2337 ) -> DispatchResult {2338 Self::remove_token_index(collection_id, item_index, old_owner)?;2339 Self::add_token_index(collection_id, item_index, new_owner)?;23402341 Ok(())2342 }2343}23442345sp_api::decl_runtime_apis! {2346 pub trait NftApi {2347 2348 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2349 }2350}