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::{DispatchError, sp_std::prelude::Vec};38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,44 MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,45 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,46 MetaUpdatePermission, FungibleItemType, ReFungibleItemType,47};4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;66pub mod weights;67use weights::WeightInfo;6869decl_error! {70 71 pub enum Error for Module<T: Config> {72 73 TotalCollectionsLimitExceeded,74 75 CollectionDecimalPointLimitExceeded,76 77 CollectionNameLimitExceeded,78 79 CollectionDescriptionLimitExceeded,80 81 CollectionTokenPrefixLimitExceeded,82 83 CollectionNotFound,84 85 TokenNotFound,86 87 AdminNotFound,88 89 NumOverflow,90 91 AlreadyAdmin,92 93 NoPermission,94 95 ConfirmUnsetSponsorFail,96 97 PublicMintingNotAllowed,98 99 MustBeTokenOwner,100 101 TokenValueTooLow,102 103 NftSizeLimitExceeded,104 105 ApproveNotFound,106 107 TokenValueNotEnough,108 109 ApproveRequired,110 111 AddresNotInWhiteList,112 113 CollectionAdminsLimitExceeded,114 115 AddressOwnershipLimitExceeded,116 117 EmptyArgument,118 119 TokenConstDataLimitExceeded,120 121 TokenVariableDataLimitExceeded,122 123 NotNftDataUsedToMintNftCollectionToken,124 125 NotFungibleDataUsedToMintFungibleCollectionToken,126 127 NotReFungibleDataUsedToMintReFungibleCollectionToken,128 129 UnexpectedCollectionType,130 131 CantStoreMetadataInFungibleTokens,132 133 CollectionTokenLimitExceeded,134 135 AccountTokenLimitExceeded,136 137 CollectionLimitBoundsExceeded,138 139 OwnerPermissionsCantBeReverted,140 141 SchemaDataLimitExceeded,142 143 WrongRefungiblePieces,144 145 BadCreateRefungibleCall,146 147 OutOfGas,148 149 MetadataUpdateDenied,150 151 MetadataFlagFrozen,152 153 TransferNotAllowed,154 155 AddressIsZero,156 }157}158159#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]160pub struct CollectionHandle<T: Config> {161 pub id: CollectionId,162 collection: Collection<T>,163 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,164}165impl<T: Config> CollectionHandle<T> {166 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {167 <CollectionById<T>>::get(id).map(|collection| Self {168 id,169 collection,170 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(171 eth::collection_id_to_address(id),172 gas_limit,173 ),174 })175 }176 pub fn get(id: CollectionId) -> Option<Self> {177 Self::get_with_gas_limit(id, u64::MAX)178 }179 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {180 self.recorder.log_sub(log)181 }182 #[allow(dead_code)]183 fn consume_gas(&self, gas: u64) -> DispatchResult {184 self.recorder.consume_gas_sub(gas)185 }186 fn consume_sload(&self) -> DispatchResult {187 self.recorder.consume_sload_sub()188 }189 fn consume_sstore(&self) -> DispatchResult {190 self.recorder.consume_sstore_sub()191 }192 pub fn submit_logs(self) -> DispatchResult {193 self.recorder.submit_logs()194 }195 pub fn save(self) -> DispatchResult {196 self.recorder.submit_logs()?;197 <CollectionById<T>>::insert(self.id, self.collection);198 Ok(())199 }200}201impl<T: Config> Deref for CollectionHandle<T> {202 type Target = Collection<T>;203204 fn deref(&self) -> &Self::Target {205 &self.collection206 }207}208209impl<T: Config> DerefMut for CollectionHandle<T> {210 fn deref_mut(&mut self) -> &mut Self::Target {211 &mut self.collection212 }213}214215pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {216 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;217218 219 type WeightInfo: WeightInfo;220221 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;222 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;223224 type CrossAccountId: CrossAccountId<Self::AccountId>;225 type Currency: Currency<Self::AccountId>;226 type CollectionCreationPrice: Get<227 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,228 >;229 type TreasuryAccountId: Get<Self::AccountId>;230}231232type SelfWeightOf<T> = <T as Config>::WeightInfo;233234trait WeightInfoHelpers: WeightInfo {235 fn transfer() -> Weight {236 Self::transfer_nft()237 .max(Self::transfer_fungible())238 .max(Self::transfer_refungible())239 }240 fn transfer_from() -> Weight {241 Self::transfer_from_nft()242 .max(Self::transfer_from_fungible())243 .max(Self::transfer_from_refungible())244 }245 fn approve() -> Weight {246 247 Self::approve_nft()248 }249 fn set_variable_meta_data(data: u32) -> Weight {250 251 Self::set_variable_meta_data_nft(data)252 }253 fn create_item(data: u32) -> Weight {254 Self::create_item_nft(data)255 .max(Self::create_item_fungible())256 .max(Self::create_item_refungible(data))257 }258 fn create_multiple_items(amount: u32) -> Weight {259 Self::create_multiple_items_nft(amount)260 .max(Self::create_multiple_items_fungible(amount))261 .max(Self::create_multiple_items_refungible(amount))262 }263 fn burn_item() -> Weight {264 265 Self::burn_item_nft()266 }267}268impl<T: WeightInfo> WeightInfoHelpers for T {}269270271272273274275276277278279280281282283284285286287288289290291292decl_storage! {293 trait Store for Module<T: Config> as Nft {294295 296 297 CreatedCollectionCount: u32;298 299 ChainVersion: u64;300 301 302 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;303 304305 306 307 308 DestroyedCollectionCount: u32;309 310 311 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;312 313314 315 316 317 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;318 319 320 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;321 322 323 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;324 325326 327 328 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;329330 331 332 333 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;334335 336 337 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;338 339 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;340 341 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;342 343344 345 346 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;347 348349 350 351 352 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;353 354 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;355 356 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;357 358 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;359 360361 362 363 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;364 }365 add_extra_genesis {366 build(|config: &GenesisConfig<T>| {367 368 for (_num, _c) in &config.collection_id {369 <Module<T>>::init_collection(_c);370 }371372 for (_num, _c, _i) in &config.nft_item_id {373 <Module<T>>::init_nft_token(*_c, _i);374 }375376 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {377 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);378 }379380 for (_num, _c, _i) in &config.refungible_item_id {381 <Module<T>>::init_refungible_token(*_c, _i);382 }383 })384 }385}386387decl_event!(388 pub enum Event<T>389 where390 AccountId = <T as frame_system::Config>::AccountId,391 CrossAccountId = <T as Config>::CrossAccountId,392 {393 394 395 396 397 398 399 400 401 402 CollectionCreated(CollectionId, u8, AccountId),403404 405 406 407 408 409 410 411 412 413 ItemCreated(CollectionId, TokenId, CrossAccountId),414415 416 417 418 419 420 421 422 ItemDestroyed(CollectionId, TokenId),423424 425 426 427 428 429 430 431 432 433 434 435 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),436437 438 439 440 441 442 443 444 445 446 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),447 }448);449450decl_module! {451 pub struct Module<T: Config> for enum Call452 where453 origin: T::Origin454 {455 fn deposit_event() = default;456 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;457 type Error = Error<T>;458459 fn on_initialize(_now: T::BlockNumber) -> Weight {460 0461 }462463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 #[weight = <SelfWeightOf<T>>::create_collection()]480 #[transactional]481 pub fn create_collection(origin,482 collection_name: Vec<u16>,483 collection_description: Vec<u16>,484 token_prefix: Vec<u8>,485 mode: CollectionMode) -> DispatchResult {486487 488 let who = ensure_signed(origin)?;489490 491 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();492 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(493 &T::TreasuryAccountId::get(),494 T::CollectionCreationPrice::get(),495 ));496 <T as Config>::Currency::settle(497 &who,498 imbalance,499 WithdrawReasons::TRANSFER,500 ExistenceRequirement::KeepAlive,501 ).map_err(|_| Error::<T>::NoPermission)?;502503 let decimal_points = match mode {504 CollectionMode::Fungible(points) => points,505 _ => 0506 };507508 let created_count = CreatedCollectionCount::get();509 let destroyed_count = DestroyedCollectionCount::get();510511 512 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);513514 515 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);516 ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);517 ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);518 ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);519520 521 let next_id = created_count522 .checked_add(1)523 .ok_or(Error::<T>::NumOverflow)?;524525 CreatedCollectionCount::put(next_id);526527 let limits = CollectionLimits {528 sponsored_data_size: CUSTOM_DATA_LIMIT,529 ..Default::default()530 };531532 533 let new_collection = Collection {534 owner: who.clone(),535 name: collection_name,536 mode: mode.clone(),537 mint_mode: false,538 access: AccessMode::Normal,539 description: collection_description,540 decimal_points,541 token_prefix,542 offchain_schema: Vec::new(),543 schema_version: SchemaVersion::ImageURL,544 sponsorship: SponsorshipState::Disabled,545 variable_on_chain_schema: Vec::new(),546 const_on_chain_schema: Vec::new(),547 limits,548 meta_update_permission: MetaUpdatePermission::default(),549 transfers_enabled: true,550 };551552 553 <CollectionById<T>>::insert(next_id, new_collection);554555 556 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));557558 Ok(())559 }560561 562 563 564 565 566 567 568 569 570 #[weight = <SelfWeightOf<T>>::destroy_collection()]571 #[transactional]572 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {573574 let sender = ensure_signed(origin)?;575 let collection = Self::get_collection(collection_id)?;576 Self::check_owner_permissions(&collection, &sender)?;577 if !collection.limits.owner_can_destroy {578 fail!(Error::<T>::NoPermission);579 }580581 <AddressTokens<T>>::remove_prefix(collection_id, None);582 <Allowances<T>>::remove_prefix(collection_id, None);583 <Balance<T>>::remove_prefix(collection_id, None);584 <ItemListIndex>::remove(collection_id);585 <AdminList<T>>::remove(collection_id);586 <CollectionById<T>>::remove(collection_id);587 <WhiteList<T>>::remove_prefix(collection_id, None);588589 <NftItemList<T>>::remove_prefix(collection_id, None);590 <FungibleItemList<T>>::remove_prefix(collection_id, None);591 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);592593 <NftTransferBasket<T>>::remove_prefix(collection_id, None);594 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);595 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);596597 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);598599 DestroyedCollectionCount::put(DestroyedCollectionCount::get()600 .checked_add(1)601 .ok_or(Error::<T>::NumOverflow)?);602603 Ok(())604 }605606 607 608 609 610 611 612 613 614 615 616 617 618 #[weight = <SelfWeightOf<T>>::add_to_white_list()]619 #[transactional]620 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{621622 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);623 let collection = Self::get_collection(collection_id)?;624625 Self::toggle_white_list_internal(626 &sender,627 &collection,628 &address,629 true,630 )?;631632 Ok(())633 }634635 636 637 638 639 640 641 642 643 644 645 646 647 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]648 #[transactional]649 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{650651 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);652 let collection = Self::get_collection(collection_id)?;653654 Self::toggle_white_list_internal(655 &sender,656 &collection,657 &address,658 false,659 )?;660661 Ok(())662 }663664 665 666 667 668 669 670 671 672 673 674 675 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]676 #[transactional]677 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult678 {679 let sender = ensure_signed(origin)?;680681 let mut target_collection = Self::get_collection(collection_id)?;682 Self::check_owner_permissions(&target_collection, &sender)?;683 target_collection.access = mode;684 target_collection.save()685 }686687 688 689 690 691 692 693 694 695 696 697 698 699 700 #[weight = <SelfWeightOf<T>>::set_mint_permission()]701 #[transactional]702 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult703 {704 let sender = ensure_signed(origin)?;705706 let mut target_collection = Self::get_collection(collection_id)?;707 Self::check_owner_permissions(&target_collection, &sender)?;708 target_collection.mint_mode = mint_permission;709 target_collection.save()710 }711712 713 714 715 716 717 718 719 720 721 722 723 #[weight = <SelfWeightOf<T>>::change_collection_owner()]724 #[transactional]725 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {726727 let sender = ensure_signed(origin)?;728 let mut target_collection = Self::get_collection(collection_id)?;729 Self::check_owner_permissions(&target_collection, &sender)?;730 target_collection.owner = new_owner;731 target_collection.save()732 }733734 735 736 737 738 739 740 741 742 743 744 745 746 747 #[weight = <SelfWeightOf<T>>::add_collection_admin()]748 #[transactional]749 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {750 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);751 let collection = Self::get_collection(collection_id)?;752 Self::check_owner_or_admin_permissions(&collection, &sender)?;753 let mut admin_arr = <AdminList<T>>::get(collection_id);754755 match admin_arr.binary_search(&new_admin_id) {756 Ok(_) => {},757 Err(idx) => {758 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);759 admin_arr.insert(idx, new_admin_id);760 <AdminList<T>>::insert(collection_id, admin_arr);761 }762 }763 Ok(())764 }765766 767 768 769 770 771 772 773 774 775 776 777 778 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]779 #[transactional]780 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {781 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);782 let collection = Self::get_collection(collection_id)?;783 Self::check_owner_or_admin_permissions(&collection, &sender)?;784 let mut admin_arr = <AdminList<T>>::get(collection_id);785786 if let Ok(idx) = admin_arr.binary_search(&account_id) {787 admin_arr.remove(idx);788 <AdminList<T>>::insert(collection_id, admin_arr);789 }790 Ok(())791 }792793 794 795 796 797 798 799 800 801 802 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]803 #[transactional]804 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {805 let sender = ensure_signed(origin)?;806 let mut target_collection = Self::get_collection(collection_id)?;807 Self::check_owner_permissions(&target_collection, &sender)?;808809 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);810 target_collection.save()811 }812813 814 815 816 817 818 819 820 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]821 #[transactional]822 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {823 let sender = ensure_signed(origin)?;824825 let mut target_collection = Self::get_collection(collection_id)?;826 ensure!(827 target_collection.sponsorship.pending_sponsor() == Some(&sender),828 Error::<T>::ConfirmUnsetSponsorFail829 );830831 target_collection.sponsorship = SponsorshipState::Confirmed(sender);832 target_collection.save()833 }834835 836 837 838 839 840 841 842 843 844 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]845 #[transactional]846 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {847 let sender = ensure_signed(origin)?;848849 let mut target_collection = Self::get_collection(collection_id)?;850 Self::check_owner_permissions(&target_collection, &sender)?;851852 target_collection.sponsorship = SponsorshipState::Disabled;853 target_collection.save()854 }855856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879880 #[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]881 #[transactional]882 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {883 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);884 let collection = Self::get_collection(collection_id)?;885886 Self::create_item_internal(&sender, &collection, &owner, data)?;887888 collection.submit_logs()889 }890891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 #[weight = <SelfWeightOf<T>>::create_multiple_items(items_data.len() as u32)]910 #[transactional]911 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {912913 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);914 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915 let collection = Self::get_collection(collection_id)?;916917 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;918919 collection.submit_logs()920 }921922 923924 925 926 927 928 929 930 931 932 933 934 935 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]936 #[transactional]937 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {938939 let sender = ensure_signed(origin)?;940 let mut target_collection = Self::get_collection(collection_id)?;941942 Self::check_owner_permissions(&target_collection, &sender)?;943944 target_collection.transfers_enabled = value;945 target_collection.save()946 }947948 949 950 951 952 953 954 955 956 957 958 959 960 #[weight = <T as Config>::WeightInfo::burn_item()]961 #[transactional]962 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {963964 let sender = ensure_signed(origin)?;965 let mut target_collection = Self::get_collection(collection_id)?;966967 ensure!(968 target_collection.meta_update_permission != MetaUpdatePermission::None,969 Error::<T>::MetadataFlagFrozen970 );971 Self::check_owner_permissions(&target_collection, &sender)?;972973 target_collection.meta_update_permission = value;974975 target_collection.save()976 }977978 979 980 981 982 983 984 985 986 987 988 989 990 991 #[weight = <SelfWeightOf<T>>::burn_item()]992 #[transactional]993 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {994995 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);996 let target_collection = Self::get_collection(collection_id)?;997998 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;9991000 target_collection.submit_logs()1001 }10021003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 #[weight = <SelfWeightOf<T>>::transfer()]1027 #[transactional]1028 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1029 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030 let collection = Self::get_collection(collection_id)?;10311032 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;10331034 collection.submit_logs()1035 }10361037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 #[weight = <SelfWeightOf<T>>::approve()]1053 #[transactional]1054 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1055 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1056 let collection = Self::get_collection(collection_id)?;10571058 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10591060 collection.submit_logs()1061 }10621063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 #[weight = <SelfWeightOf<T>>::transfer_from()]1083 #[transactional]1084 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1085 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1086 let collection = Self::get_collection(collection_id)?;10871088 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10891090 collection.submit_logs()1091 }1092 1093 1094 1095 1096 10971098 10991100 11011102 1103 11041105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1118 #[transactional]1119 pub fn set_variable_meta_data (1120 origin,1121 collection_id: CollectionId,1122 item_id: TokenId,1123 data: Vec<u8>1124 ) -> DispatchResult {1125 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11261127 let collection = Self::get_collection(collection_id)?;11281129 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;11301131 Ok(())1132 }11331134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 #[weight = <SelfWeightOf<T>>::set_schema_version()]1149 #[transactional]1150 pub fn set_schema_version(1151 origin,1152 collection_id: CollectionId,1153 version: SchemaVersion1154 ) -> DispatchResult {1155 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1156 let mut target_collection = Self::get_collection(collection_id)?;1157 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1158 target_collection.schema_version = version;1159 target_collection.save()1160 }11611162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1175 #[transactional]1176 pub fn set_offchain_schema(1177 origin,1178 collection_id: CollectionId,1179 schema: Vec<u8>1180 ) -> DispatchResult {1181 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1182 let mut target_collection = Self::get_collection(collection_id)?;1183 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11841185 1186 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11871188 target_collection.offchain_schema = schema;1189 target_collection.save()1190 }11911192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1205 #[transactional]1206 pub fn set_const_on_chain_schema (1207 origin,1208 collection_id: CollectionId,1209 schema: Vec<u8>1210 ) -> DispatchResult {1211 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1212 let mut target_collection = Self::get_collection(collection_id)?;1213 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12141215 1216 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");12171218 target_collection.const_on_chain_schema = schema;1219 target_collection.save()1220 }12211222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1235 #[transactional]1236 pub fn set_variable_on_chain_schema (1237 origin,1238 collection_id: CollectionId,1239 schema: Vec<u8>1240 ) -> DispatchResult {1241 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1242 let mut target_collection = Self::get_collection(collection_id)?;1243 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12441245 1246 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12471248 target_collection.variable_on_chain_schema = schema;1249 target_collection.save()1250 }12511252 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1253 #[transactional]1254 pub fn set_collection_limits(1255 origin,1256 collection_id: u32,1257 new_limits: CollectionLimits<T::BlockNumber>,1258 ) -> DispatchResult {1259 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1260 let mut target_collection = Self::get_collection(collection_id)?;1261 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1262 let old_limits = &target_collection.limits;12631264 1265 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1266 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1267 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1268 Error::<T>::CollectionLimitBoundsExceeded);12691270 1271 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1272 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12731274 ensure!(1275 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1276 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1277 Error::<T>::OwnerPermissionsCantBeReverted,1278 );12791280 target_collection.limits = new_limits;12811282 target_collection.save()1283 }1284 }1285}12861287impl<T: Config> Module<T> {1288 pub fn create_item_internal(1289 sender: &T::CrossAccountId,1290 collection: &CollectionHandle<T>,1291 owner: &T::CrossAccountId,1292 data: CreateItemData,1293 ) -> DispatchResult {1294 ensure!(1295 owner != &T::CrossAccountId::from_eth(H160([0; 20])),1296 Error::<T>::AddressIsZero1297 );12981299 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1300 Self::validate_create_item_args(collection, &data)?;1301 Self::create_item_no_validation(collection, owner, data)?;13021303 Ok(())1304 }13051306 pub fn transfer_internal(1307 sender: &T::CrossAccountId,1308 recipient: &T::CrossAccountId,1309 target_collection: &CollectionHandle<T>,1310 item_id: TokenId,1311 value: u128,1312 ) -> DispatchResult {1313 ensure!(1314 recipient != &T::CrossAccountId::from_eth(H160([0; 20])),1315 Error::<T>::AddressIsZero1316 );13171318 1319 Self::is_correct_transfer(target_collection, recipient)?;13201321 1322 ensure!(1323 Self::is_item_owner(sender, target_collection, item_id)?1324 || Self::is_owner_or_admin_permissions(target_collection, sender)?,1325 Error::<T>::NoPermission1326 );13271328 if target_collection.access == AccessMode::WhiteList {1329 Self::check_white_list(target_collection, sender)?;1330 Self::check_white_list(target_collection, recipient)?;1331 }13321333 match target_collection.mode {1334 CollectionMode::NFT => Self::transfer_nft(1335 target_collection,1336 item_id,1337 sender.clone(),1338 recipient.clone(),1339 )?,1340 CollectionMode::Fungible(_) => {1341 Self::transfer_fungible(target_collection, value, sender, recipient)?1342 }1343 CollectionMode::ReFungible => Self::transfer_refungible(1344 target_collection,1345 item_id,1346 value,1347 sender.clone(),1348 recipient.clone(),1349 )?,1350 };13511352 Self::deposit_event(RawEvent::Transfer(1353 target_collection.id,1354 item_id,1355 sender.clone(),1356 recipient.clone(),1357 value,1358 ));13591360 Ok(())1361 }13621363 pub fn approve_internal(1364 sender: &T::CrossAccountId,1365 spender: &T::CrossAccountId,1366 collection: &CollectionHandle<T>,1367 item_id: TokenId,1368 amount: u128,1369 ) -> DispatchResult {1370 Self::token_exists(collection, item_id)?;13711372 1373 let bypasses_limits = collection.limits.owner_can_transfer1374 && Self::is_owner_or_admin_permissions(collection, sender)?;13751376 let allowance_limit = if bypasses_limits {1377 None1378 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id)? {1379 Some(amount)1380 } else {1381 fail!(Error::<T>::NoPermission);1382 };13831384 if collection.access == AccessMode::WhiteList {1385 Self::check_white_list(collection, sender)?;1386 Self::check_white_list(collection, spender)?;1387 }13881389 collection.consume_sload()?;1390 let allowance: u128 = amount1391 .checked_add(<Allowances<T>>::get(1392 collection.id,1393 (item_id, sender.as_sub(), spender.as_sub()),1394 ))1395 .ok_or(Error::<T>::NumOverflow)?;1396 if let Some(limit) = allowance_limit {1397 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1398 }1399 collection.consume_sstore()?;1400 <Allowances<T>>::insert(1401 collection.id,1402 (item_id, sender.as_sub(), spender.as_sub()),1403 allowance,1404 );14051406 if matches!(collection.mode, CollectionMode::NFT) {1407 1408 collection.log(ERC721Events::Approval {1409 owner: *sender.as_eth(),1410 approved: *spender.as_eth(),1411 token_id: item_id.into(),1412 })?;1413 }14141415 if matches!(collection.mode, CollectionMode::Fungible(_)) {1416 1417 collection.log(ERC20Events::Approval {1418 owner: *sender.as_eth(),1419 spender: *spender.as_eth(),1420 value: allowance.into(),1421 })?;1422 }14231424 Self::deposit_event(RawEvent::Approved(1425 collection.id,1426 item_id,1427 sender.clone(),1428 spender.clone(),1429 allowance,1430 ));1431 Ok(())1432 }14331434 pub fn transfer_from_internal(1435 sender: &T::CrossAccountId,1436 from: &T::CrossAccountId,1437 recipient: &T::CrossAccountId,1438 collection: &CollectionHandle<T>,1439 item_id: TokenId,1440 amount: u128,1441 ) -> DispatchResult {1442 if sender == from {1443 1444 return Self::transfer_internal(from, recipient, collection, item_id, amount);1445 }14461447 1448 collection.consume_sload()?;1449 let approval: u128 =1450 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));14511452 1453 Self::is_correct_transfer(collection, recipient)?;14541455 1456 ensure!(1457 approval >= amount1458 || (collection.limits.owner_can_transfer1459 && Self::is_owner_or_admin_permissions(collection, sender)?),1460 Error::<T>::NoPermission1461 );14621463 if collection.access == AccessMode::WhiteList {1464 Self::check_white_list(collection, sender)?;1465 Self::check_white_list(collection, recipient)?;1466 }14671468 1469 let allowance = approval.saturating_sub(amount);1470 collection.consume_sstore()?;1471 if allowance > 0 {1472 <Allowances<T>>::insert(1473 collection.id,1474 (item_id, from.as_sub(), sender.as_sub()),1475 allowance,1476 );1477 } else {1478 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1479 }14801481 match collection.mode {1482 CollectionMode::NFT => {1483 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1484 }1485 CollectionMode::Fungible(_) => {1486 Self::transfer_fungible(collection, amount, from, recipient)?1487 }1488 CollectionMode::ReFungible => Self::transfer_refungible(1489 collection,1490 item_id,1491 amount,1492 from.clone(),1493 recipient.clone(),1494 )?,1495 };14961497 if matches!(collection.mode, CollectionMode::Fungible(_)) {1498 collection.log(ERC20Events::Approval {1499 owner: *from.as_eth(),1500 spender: *sender.as_eth(),1501 value: allowance.into(),1502 })?;1503 }15041505 Ok(())1506 }15071508 pub fn set_variable_meta_data_internal(1509 sender: &T::CrossAccountId,1510 collection: &CollectionHandle<T>,1511 item_id: TokenId,1512 data: Vec<u8>,1513 ) -> DispatchResult {1514 Self::token_exists(collection, item_id)?;15151516 ensure!(1517 CUSTOM_DATA_LIMIT >= data.len() as u32,1518 Error::<T>::TokenVariableDataLimitExceeded1519 );15201521 ensure!(1522 (Self::is_item_owner(sender, collection, item_id)?1523 && collection.meta_update_permission == MetaUpdatePermission::ItemOwner)1524 || (Self::is_owner_or_admin_permissions(collection, sender)?1525 && collection.meta_update_permission == MetaUpdatePermission::Admin),1526 Error::<T>::NoPermission1527 );15281529 match collection.mode {1530 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1531 CollectionMode::ReFungible => {1532 Self::set_re_fungible_variable_data(collection, item_id, data)?1533 }1534 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1535 };15361537 Ok(())1538 }15391540 pub fn meta_update_check(1541 sender: &T::CrossAccountId,1542 collection: &CollectionHandle<T>,1543 item_id: TokenId,1544 ) -> DispatchResult {1545 match collection.meta_update_permission {1546 MetaUpdatePermission::ItemOwner => ensure!(1547 Self::is_item_owner(sender, collection, item_id)?,1548 Error::<T>::NoPermission1549 ),1550 MetaUpdatePermission::Admin => ensure!(1551 Self::is_owner_or_admin_permissions(collection, sender)?,1552 Error::<T>::NoPermission1553 ),1554 MetaUpdatePermission::None => fail!(Error::<T>::MetadataUpdateDenied),1555 }15561557 Ok(())1558 }15591560 pub fn get_variable_metadata(1561 collection: &CollectionHandle<T>,1562 item_id: TokenId,1563 ) -> Result<Vec<u8>, DispatchError> {1564 Ok(match collection.mode {1565 CollectionMode::NFT => {1566 <NftItemList<T>>::get(collection.id, item_id)1567 .ok_or(Error::<T>::TokenNotFound)?1568 .variable_data1569 }1570 CollectionMode::ReFungible => {1571 <ReFungibleItemList<T>>::get(collection.id, item_id)1572 .ok_or(Error::<T>::TokenNotFound)?1573 .variable_data1574 }1575 _ => fail!(Error::<T>::UnexpectedCollectionType),1576 })1577 }15781579 pub fn create_multiple_items_internal(1580 sender: &T::CrossAccountId,1581 collection: &CollectionHandle<T>,1582 owner: &T::CrossAccountId,1583 items_data: Vec<CreateItemData>,1584 ) -> DispatchResult {1585 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;15861587 for data in &items_data {1588 Self::validate_create_item_args(collection, data)?;1589 }1590 for data in &items_data {1591 Self::create_item_no_validation(collection, owner, data.clone())?;1592 }15931594 Ok(())1595 }15961597 pub fn burn_item_internal(1598 sender: &T::CrossAccountId,1599 collection: &CollectionHandle<T>,1600 item_id: TokenId,1601 value: u128,1602 ) -> DispatchResult {1603 ensure!(1604 Self::is_item_owner(sender, collection, item_id)?1605 || (collection.limits.owner_can_transfer1606 && Self::is_owner_or_admin_permissions(collection, sender)?),1607 Error::<T>::NoPermission1608 );16091610 if collection.access == AccessMode::WhiteList {1611 Self::check_white_list(collection, sender)?;1612 }16131614 match collection.mode {1615 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1616 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1617 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1618 };16191620 Ok(())1621 }16221623 pub fn toggle_white_list_internal(1624 sender: &T::CrossAccountId,1625 collection: &CollectionHandle<T>,1626 address: &T::CrossAccountId,1627 whitelisted: bool,1628 ) -> DispatchResult {1629 Self::check_owner_or_admin_permissions(collection, sender)?;16301631 if whitelisted {1632 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1633 } else {1634 <WhiteList<T>>::remove(collection.id, address.as_sub());1635 }16361637 Ok(())1638 }16391640 fn is_correct_transfer(1641 collection: &CollectionHandle<T>,1642 recipient: &T::CrossAccountId,1643 ) -> DispatchResult {1644 let collection_id = collection.id;16451646 1647 collection.consume_sload()?;1648 let account_items: u32 =1649 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1650 ensure!(1651 collection.limits.account_token_ownership_limit > account_items,1652 Error::<T>::AccountTokenLimitExceeded1653 );16541655 1656 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);16571658 Ok(())1659 }16601661 fn can_create_items_in_collection(1662 collection: &CollectionHandle<T>,1663 sender: &T::CrossAccountId,1664 owner: &T::CrossAccountId,1665 amount: u32,1666 ) -> DispatchResult {1667 let collection_id = collection.id;16681669 1670 let total_items: u32 = ItemListIndex::get(collection_id)1671 .checked_add(amount)1672 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1673 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1674 as u32)1675 .checked_add(amount)1676 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1677 ensure!(1678 collection.limits.token_limit >= total_items,1679 Error::<T>::CollectionTokenLimitExceeded1680 );1681 ensure!(1682 collection.limits.account_token_ownership_limit >= account_items,1683 Error::<T>::AccountTokenLimitExceeded1684 );16851686 if !Self::is_owner_or_admin_permissions(collection, sender)? {1687 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1688 Self::check_white_list(collection, owner)?;1689 Self::check_white_list(collection, sender)?;1690 }16911692 Ok(())1693 }16941695 fn validate_create_item_args(1696 target_collection: &CollectionHandle<T>,1697 data: &CreateItemData,1698 ) -> DispatchResult {1699 match target_collection.mode {1700 CollectionMode::NFT => {1701 if !matches!(data, CreateItemData::NFT(_)) {1702 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1703 }1704 }1705 CollectionMode::Fungible(_) => {1706 if !matches!(data, CreateItemData::Fungible(_)) {1707 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1708 }1709 }1710 CollectionMode::ReFungible => {1711 if let CreateItemData::ReFungible(data) = data {1712 1713 ensure!(1714 data.pieces <= MAX_REFUNGIBLE_PIECES,1715 Error::<T>::WrongRefungiblePieces1716 );1717 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1718 } else {1719 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1720 }1721 }1722 };17231724 Ok(())1725 }17261727 fn create_item_no_validation(1728 collection: &CollectionHandle<T>,1729 owner: &T::CrossAccountId,1730 data: CreateItemData,1731 ) -> DispatchResult {1732 match data {1733 CreateItemData::NFT(data) => {1734 let item = NftItemType {1735 owner: owner.clone(),1736 const_data: data.const_data.into_inner(),1737 variable_data: data.variable_data.into_inner(),1738 };17391740 Self::add_nft_item(collection, item)?;1741 }1742 CreateItemData::Fungible(data) => {1743 Self::add_fungible_item(collection, owner, data.value)?;1744 }1745 CreateItemData::ReFungible(data) => {1746 let owner_list = vec![Ownership {1747 owner: owner.clone(),1748 fraction: data.pieces,1749 }];17501751 let item = ReFungibleItemType {1752 owner: owner_list,1753 const_data: data.const_data.into_inner(),1754 variable_data: data.variable_data.into_inner(),1755 };17561757 Self::add_refungible_item(collection, item)?;1758 }1759 };17601761 Ok(())1762 }17631764 fn add_fungible_item(1765 collection: &CollectionHandle<T>,1766 owner: &T::CrossAccountId,1767 value: u128,1768 ) -> DispatchResult {1769 let collection_id = collection.id;17701771 1772 collection.consume_sload()?;1773 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17741775 1776 let item = FungibleItemType {1777 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1778 };1779 collection.consume_sstore()?;1780 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17811782 1783 collection.consume_sload()?;1784 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1785 .checked_add(value)1786 .ok_or(Error::<T>::NumOverflow)?;1787 collection.consume_sstore()?;1788 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17891790 collection.log(ERC20Events::Transfer {1791 from: H160::default(),1792 to: *owner.as_eth(),1793 value: value.into(),1794 })?;1795 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1796 Ok(())1797 }17981799 fn add_refungible_item(1800 collection: &CollectionHandle<T>,1801 item: ReFungibleItemType<T::CrossAccountId>,1802 ) -> DispatchResult {1803 let collection_id = collection.id;18041805 let current_index = <ItemListIndex>::get(collection_id)1806 .checked_add(1)1807 .ok_or(Error::<T>::NumOverflow)?;1808 let itemcopy = item.clone();18091810 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1811 let item_owner = item.owner.first().expect("only one owner is defined");18121813 let value = item_owner.fraction;1814 let owner = item_owner.owner.clone();18151816 Self::add_token_index(collection, current_index, &owner)?;18171818 <ItemListIndex>::insert(collection_id, current_index);1819 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18201821 1822 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1823 .checked_add(value)1824 .ok_or(Error::<T>::NumOverflow)?;1825 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18261827 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1828 Ok(())1829 }18301831 fn add_nft_item(1832 collection: &CollectionHandle<T>,1833 item: NftItemType<T::CrossAccountId>,1834 ) -> DispatchResult {1835 let collection_id = collection.id;18361837 let current_index = <ItemListIndex>::get(collection_id)1838 .checked_add(1)1839 .ok_or(Error::<T>::NumOverflow)?;18401841 let item_owner = item.owner.clone();1842 Self::add_token_index(collection, current_index, &item.owner)?;18431844 <ItemListIndex>::insert(collection_id, current_index);1845 <NftItemList<T>>::insert(collection_id, current_index, item);18461847 1848 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1849 .checked_add(1)1850 .ok_or(Error::<T>::NumOverflow)?;1851 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18521853 collection.log(ERC721Events::Transfer {1854 from: H160::default(),1855 to: *item_owner.as_eth(),1856 token_id: current_index.into(),1857 })?;1858 Self::deposit_event(RawEvent::ItemCreated(1859 collection_id,1860 current_index,1861 item_owner,1862 ));1863 Ok(())1864 }18651866 fn burn_refungible_item(1867 collection: &CollectionHandle<T>,1868 item_id: TokenId,1869 owner: &T::CrossAccountId,1870 ) -> DispatchResult {1871 let collection_id = collection.id;18721873 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1874 .ok_or(Error::<T>::TokenNotFound)?;1875 let rft_balance = token1876 .owner1877 .iter()1878 .find(|&i| i.owner == *owner)1879 .ok_or(Error::<T>::TokenNotFound)?;1880 Self::remove_token_index(collection, item_id, owner)?;18811882 1883 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1884 .checked_sub(rft_balance.fraction)1885 .ok_or(Error::<T>::NumOverflow)?;1886 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18871888 1889 let index = token1890 .owner1891 .iter()1892 .position(|i| i.owner == *owner)1893 .expect("owned item is exists");1894 token.owner.remove(index);1895 let owner_count = token.owner.len();18961897 1898 if owner_count == 0 {1899 <ReFungibleItemList<T>>::remove(collection_id, item_id);1900 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1901 } else {1902 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1903 }19041905 Ok(())1906 }19071908 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1909 let collection_id = collection.id;19101911 let item =1912 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1913 Self::remove_token_index(collection, item_id, &item.owner)?;19141915 1916 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1917 .checked_sub(1)1918 .ok_or(Error::<T>::NumOverflow)?;1919 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1920 <NftItemList<T>>::remove(collection_id, item_id);1921 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);19221923 collection.log(ERC721Events::Transfer {1924 from: *item.owner.as_eth(),1925 to: H160::default(),1926 token_id: item_id.into(),1927 })?;1928 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1929 Ok(())1930 }19311932 fn burn_fungible_item(1933 owner: &T::CrossAccountId,1934 collection: &CollectionHandle<T>,1935 value: u128,1936 ) -> DispatchResult {1937 let collection_id = collection.id;19381939 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1940 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19411942 1943 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1944 .checked_sub(value)1945 .ok_or(Error::<T>::NumOverflow)?;1946 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19471948 if balance.value - value > 0 {1949 balance.value -= value;1950 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1951 } else {1952 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1953 }19541955 collection.log(ERC20Events::Transfer {1956 from: *owner.as_eth(),1957 to: H160::default(),1958 value: value.into(),1959 })?;1960 Ok(())1961 }19621963 pub fn get_collection(1964 collection_id: CollectionId,1965 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1966 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1967 }19681969 fn check_owner_permissions(1970 target_collection: &CollectionHandle<T>,1971 subject: &T::AccountId,1972 ) -> DispatchResult {1973 ensure!(1974 *subject == target_collection.owner,1975 Error::<T>::NoPermission1976 );19771978 Ok(())1979 }19801981 fn is_owner_or_admin_permissions(1982 collection: &CollectionHandle<T>,1983 subject: &T::CrossAccountId,1984 ) -> Result<bool, DispatchError> {1985 collection.consume_sload()?;1986 Ok(*subject.as_sub() == collection.owner1987 || <AdminList<T>>::get(collection.id).contains(subject))1988 }19891990 fn check_owner_or_admin_permissions(1991 collection: &CollectionHandle<T>,1992 subject: &T::CrossAccountId,1993 ) -> DispatchResult {1994 ensure!(1995 Self::is_owner_or_admin_permissions(collection, subject)?,1996 Error::<T>::NoPermission1997 );19981999 Ok(())2000 }20012002 fn owned_amount(2003 subject: &T::CrossAccountId,2004 collection: &CollectionHandle<T>,2005 item_id: TokenId,2006 ) -> Result<Option<u128>, DispatchError> {2007 collection.consume_sload()?;2008 Ok(Self::owned_amount_unchecked(subject, collection, item_id))2009 }20102011 fn owned_amount_unchecked(2012 subject: &T::CrossAccountId,2013 target_collection: &CollectionHandle<T>,2014 item_id: TokenId,2015 ) -> Option<u128> {2016 let collection_id = target_collection.id;20172018 match target_collection.mode {2019 CollectionMode::NFT => {2020 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)2021 }2022 CollectionMode::Fungible(_) => {2023 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)2024 }2025 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2026 .owner2027 .iter()2028 .find(|i| i.owner == *subject)2029 .map(|i| i.fraction),2030 }2031 }20322033 fn is_item_owner(2034 subject: &T::CrossAccountId,2035 target_collection: &CollectionHandle<T>,2036 item_id: TokenId,2037 ) -> Result<bool, DispatchError> {2038 Ok(match target_collection.mode {2039 CollectionMode::Fungible(_) => true,2040 _ => Self::owned_amount(subject, target_collection, item_id)?.is_some(),2041 })2042 }20432044 fn check_white_list(2045 collection: &CollectionHandle<T>,2046 address: &T::CrossAccountId,2047 ) -> DispatchResult {2048 collection.consume_sload()?;2049 ensure!(2050 <WhiteList<T>>::contains_key(collection.id, address.as_sub()),2051 Error::<T>::AddresNotInWhiteList,2052 );2053 Ok(())2054 }20552056 2057 2058 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2059 let collection_id = target_collection.id;2060 let exists = match target_collection.mode {2061 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2062 CollectionMode::Fungible(_) => true,2063 CollectionMode::ReFungible => {2064 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)2065 }2066 };20672068 ensure!(exists, Error::<T>::TokenNotFound);2069 Ok(())2070 }20712072 fn transfer_fungible(2073 collection: &CollectionHandle<T>,2074 value: u128,2075 owner: &T::CrossAccountId,2076 recipient: &T::CrossAccountId,2077 ) -> DispatchResult {2078 let collection_id = collection.id;20792080 collection.consume_sload()?;2081 collection.consume_sload()?;2082 let mut recipient_balance = <FungibleItemList<T>>::get(collection_id, recipient.as_sub());2083 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());20842085 recipient_balance.value = recipient_balance2086 .value2087 .checked_add(value)2088 .ok_or(Error::<T>::NumOverflow)?;2089 balance.value = balance2090 .value2091 .checked_sub(value)2092 .ok_or(Error::<T>::TokenValueTooLow)?;20932094 2095 collection.consume_sstore()?;2096 collection.consume_sstore()?;2097 if balance.value != 0 {2098 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value);2099 } else {2100 <Balance<T>>::remove(collection_id, owner.as_sub());2101 }2102 <Balance<T>>::insert(collection_id, recipient.as_sub(), recipient_balance.value);21032104 2105 collection.consume_sstore()?;2106 collection.consume_sstore()?;2107 if balance.value != 0 {2108 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2109 } else {2110 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2111 }2112 <FungibleItemList<T>>::insert(collection_id, recipient.as_sub(), recipient_balance);21132114 collection.log(ERC20Events::Transfer {2115 from: *owner.as_eth(),2116 to: *recipient.as_eth(),2117 value: value.into(),2118 })?;2119 Self::deposit_event(RawEvent::Transfer(2120 collection.id,2121 1,2122 owner.clone(),2123 recipient.clone(),2124 value,2125 ));21262127 Ok(())2128 }21292130 fn transfer_refungible(2131 collection: &CollectionHandle<T>,2132 item_id: TokenId,2133 value: u128,2134 owner: T::CrossAccountId,2135 new_owner: T::CrossAccountId,2136 ) -> DispatchResult {2137 let collection_id = collection.id;2138 collection.consume_sload()?;2139 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2140 .ok_or(Error::<T>::TokenNotFound)?;21412142 let item = full_item2143 .owner2144 .iter()2145 .find(|i| i.owner == owner)2146 .ok_or(Error::<T>::TokenNotFound)?;2147 let amount = item.fraction;21482149 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21502151 collection.consume_sload()?;2152 2153 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2154 .checked_sub(value)2155 .ok_or(Error::<T>::NumOverflow)?;2156 collection.consume_sstore()?;2157 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21582159 collection.consume_sload()?;2160 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2161 .checked_add(value)2162 .ok_or(Error::<T>::NumOverflow)?;2163 collection.consume_sstore()?;2164 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21652166 let old_owner = item.owner.clone();2167 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21682169 let mut new_full_item = full_item.clone();2170 2171 if amount == value && !new_owner_has_account {2172 2173 2174 new_full_item2175 .owner2176 .iter_mut()2177 .find(|i| i.owner == owner)2178 .expect("old owner does present in refungible")2179 .owner = new_owner.clone();2180 collection.consume_sstore()?;2181 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21822183 2184 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;2185 } else {2186 new_full_item2187 .owner2188 .iter_mut()2189 .find(|i| i.owner == owner)2190 .expect("old owner does present in refungible")2191 .fraction -= value;21922193 2194 if new_owner_has_account {2195 2196 new_full_item2197 .owner2198 .iter_mut()2199 .find(|i| i.owner == new_owner)2200 .expect("new owner has account")2201 .fraction += value;2202 } else {2203 2204 new_full_item.owner.push(Ownership {2205 owner: new_owner.clone(),2206 fraction: value,2207 });2208 Self::add_token_index(collection, item_id, &new_owner)?;2209 }22102211 collection.consume_sstore()?;2212 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2213 }22142215 Self::deposit_event(RawEvent::Transfer(2216 collection.id,2217 item_id,2218 owner,2219 new_owner,2220 amount,2221 ));22222223 Ok(())2224 }22252226 fn transfer_nft(2227 collection: &CollectionHandle<T>,2228 item_id: TokenId,2229 sender: T::CrossAccountId,2230 new_owner: T::CrossAccountId,2231 ) -> DispatchResult {2232 let collection_id = collection.id;2233 collection.consume_sload()?;2234 let mut item =2235 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;22362237 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);22382239 collection.consume_sload()?;2240 2241 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2242 .checked_sub(1)2243 .ok_or(Error::<T>::NumOverflow)?;2244 collection.consume_sstore()?;2245 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);22462247 collection.consume_sload()?;2248 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2249 .checked_add(1)2250 .ok_or(Error::<T>::NumOverflow)?;2251 collection.consume_sstore()?;2252 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);22532254 2255 let old_owner = item.owner.clone();2256 item.owner = new_owner.clone();2257 collection.consume_sstore()?;2258 <NftItemList<T>>::insert(collection_id, item_id, item);22592260 2261 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;22622263 collection.log(ERC721Events::Transfer {2264 from: *sender.as_eth(),2265 to: *new_owner.as_eth(),2266 token_id: item_id.into(),2267 })?;2268 Self::deposit_event(RawEvent::Transfer(2269 collection.id,2270 item_id,2271 sender,2272 new_owner,2273 1,2274 ));22752276 Ok(())2277 }22782279 fn set_re_fungible_variable_data(2280 collection: &CollectionHandle<T>,2281 item_id: TokenId,2282 data: Vec<u8>,2283 ) -> DispatchResult {2284 let collection_id = collection.id;2285 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2286 .ok_or(Error::<T>::TokenNotFound)?;22872288 item.variable_data = data;22892290 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22912292 Ok(())2293 }22942295 fn set_nft_variable_data(2296 collection: &CollectionHandle<T>,2297 item_id: TokenId,2298 data: Vec<u8>,2299 ) -> DispatchResult {2300 let collection_id = collection.id;2301 let mut item =2302 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;23032304 item.variable_data = data;23052306 <NftItemList<T>>::insert(collection_id, item_id, item);23072308 Ok(())2309 }23102311 #[allow(dead_code)]2312 fn init_collection(item: &Collection<T>) {2313 2314 assert!(2315 item.decimal_points <= MAX_DECIMAL_POINTS,2316 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2317 );2318 assert!(2319 item.name.len() <= 64,2320 "Collection name can not be longer than 63 char"2321 );2322 assert!(2323 item.name.len() <= 256,2324 "Collection description can not be longer than 255 char"2325 );2326 assert!(2327 item.token_prefix.len() <= 16,2328 "Token prefix can not be longer than 15 char"2329 );23302331 2332 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();23332334 CreatedCollectionCount::put(next_id);2335 }23362337 #[allow(dead_code)]2338 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2339 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23402341 Self::add_token_index(2342 &CollectionHandle::get(collection_id).unwrap(),2343 current_index,2344 &item.owner,2345 )2346 .unwrap();23472348 <ItemListIndex>::insert(collection_id, current_index);23492350 2351 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2352 .checked_add(1)2353 .unwrap();2354 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2355 }23562357 #[allow(dead_code)]2358 fn init_fungible_token(2359 collection_id: CollectionId,2360 owner: &T::CrossAccountId,2361 item: &FungibleItemType,2362 ) {2363 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23642365 Self::add_token_index(2366 &CollectionHandle::get(collection_id).unwrap(),2367 current_index,2368 owner,2369 )2370 .unwrap();23712372 <ItemListIndex>::insert(collection_id, current_index);23732374 2375 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2376 .checked_add(item.value)2377 .unwrap();2378 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2379 }23802381 #[allow(dead_code)]2382 fn init_refungible_token(2383 collection_id: CollectionId,2384 item: &ReFungibleItemType<T::CrossAccountId>,2385 ) {2386 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23872388 let value = item.owner.first().unwrap().fraction;2389 let owner = item.owner.first().unwrap().owner.clone();23902391 Self::add_token_index(2392 &CollectionHandle::get(collection_id).unwrap(),2393 current_index,2394 &owner,2395 )2396 .unwrap();23972398 <ItemListIndex>::insert(collection_id, current_index);23992400 2401 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2402 .checked_add(value)2403 .unwrap();2404 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2405 }24062407 fn add_token_index(2408 collection: &CollectionHandle<T>,2409 item_index: TokenId,2410 owner: &T::CrossAccountId,2411 ) -> DispatchResult {2412 2413 collection.consume_sload()?;2414 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2415 2416 collection.consume_sload()?;2417 let count = <AccountItemCount<T>>::get(owner.as_sub());2418 ensure!(2419 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2420 Error::<T>::AddressOwnershipLimitExceeded2421 );24222423 collection.consume_sstore()?;2424 <AccountItemCount<T>>::insert(2425 owner.as_sub(),2426 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2427 );2428 } else {2429 collection.consume_sstore()?;2430 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2431 }24322433 collection.consume_sload()?;2434 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2435 if list_exists {2436 collection.consume_sload()?;2437 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2438 let item_contains = list.contains(&item_index.clone());24392440 if !item_contains {2441 list.push(item_index);2442 }24432444 collection.consume_sstore()?;2445 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2446 } else {2447 let itm = vec![item_index];2448 collection.consume_sstore()?;2449 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), itm);2450 }24512452 Ok(())2453 }24542455 fn remove_token_index(2456 collection: &CollectionHandle<T>,2457 item_index: TokenId,2458 owner: &T::CrossAccountId,2459 ) -> DispatchResult {2460 2461 collection.consume_sload()?;2462 collection.consume_sstore()?;2463 <AccountItemCount<T>>::insert(2464 owner.as_sub(),2465 <AccountItemCount<T>>::get(owner.as_sub())2466 .checked_sub(1)2467 .ok_or(Error::<T>::NumOverflow)?,2468 );24692470 collection.consume_sload()?;2471 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2472 if list_exists {2473 collection.consume_sload()?;2474 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2475 let item_contains = list.contains(&item_index.clone());24762477 if item_contains {2478 list.retain(|&item| item != item_index);2479 collection.consume_sstore()?;2480 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2481 }2482 }24832484 Ok(())2485 }24862487 fn move_token_index(2488 collection: &CollectionHandle<T>,2489 item_index: TokenId,2490 old_owner: &T::CrossAccountId,2491 new_owner: &T::CrossAccountId,2492 ) -> DispatchResult {2493 Self::remove_token_index(collection, item_index, old_owner)?;2494 Self::add_token_index(collection, item_index, new_owner)?;24952496 Ok(())2497 }2498}24992500sp_api::decl_runtime_apis! {2501 pub trait NftApi {2502 2503 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2504 }2505}