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 _ => (),1351 };13521353 Self::deposit_event(RawEvent::Transfer(1354 target_collection.id,1355 item_id,1356 sender.clone(),1357 recipient.clone(),1358 value,1359 ));13601361 Ok(())1362 }13631364 pub fn approve_internal(1365 sender: &T::CrossAccountId,1366 spender: &T::CrossAccountId,1367 collection: &CollectionHandle<T>,1368 item_id: TokenId,1369 amount: u128,1370 ) -> DispatchResult {1371 Self::token_exists(collection, item_id)?;13721373 1374 let bypasses_limits = collection.limits.owner_can_transfer1375 && Self::is_owner_or_admin_permissions(collection, sender)?;13761377 let allowance_limit = if bypasses_limits {1378 None1379 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id)? {1380 Some(amount)1381 } else {1382 fail!(Error::<T>::NoPermission);1383 };13841385 if collection.access == AccessMode::WhiteList {1386 Self::check_white_list(collection, sender)?;1387 Self::check_white_list(collection, spender)?;1388 }13891390 collection.consume_sload()?;1391 let allowance: u128 = amount1392 .checked_add(<Allowances<T>>::get(1393 collection.id,1394 (item_id, sender.as_sub(), spender.as_sub()),1395 ))1396 .ok_or(Error::<T>::NumOverflow)?;1397 if let Some(limit) = allowance_limit {1398 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1399 }1400 collection.consume_sstore()?;1401 <Allowances<T>>::insert(1402 collection.id,1403 (item_id, sender.as_sub(), spender.as_sub()),1404 allowance,1405 );14061407 if matches!(collection.mode, CollectionMode::NFT) {1408 1409 collection.log(ERC721Events::Approval {1410 owner: *sender.as_eth(),1411 approved: *spender.as_eth(),1412 token_id: item_id.into(),1413 })?;1414 }14151416 if matches!(collection.mode, CollectionMode::Fungible(_)) {1417 1418 collection.log(ERC20Events::Approval {1419 owner: *sender.as_eth(),1420 spender: *spender.as_eth(),1421 value: allowance.into(),1422 })?;1423 }14241425 Self::deposit_event(RawEvent::Approved(1426 collection.id,1427 item_id,1428 sender.clone(),1429 spender.clone(),1430 allowance,1431 ));1432 Ok(())1433 }14341435 pub fn transfer_from_internal(1436 sender: &T::CrossAccountId,1437 from: &T::CrossAccountId,1438 recipient: &T::CrossAccountId,1439 collection: &CollectionHandle<T>,1440 item_id: TokenId,1441 amount: u128,1442 ) -> DispatchResult {1443 if sender == from {1444 1445 return Self::transfer_internal(from, recipient, collection, item_id, amount);1446 }14471448 1449 collection.consume_sload()?;1450 let approval: u128 =1451 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));14521453 1454 Self::is_correct_transfer(collection, recipient)?;14551456 1457 ensure!(1458 approval >= amount1459 || (collection.limits.owner_can_transfer1460 && Self::is_owner_or_admin_permissions(collection, sender)?),1461 Error::<T>::NoPermission1462 );14631464 if collection.access == AccessMode::WhiteList {1465 Self::check_white_list(collection, sender)?;1466 Self::check_white_list(collection, recipient)?;1467 }14681469 1470 let allowance = approval.saturating_sub(amount);1471 collection.consume_sstore()?;1472 if allowance > 0 {1473 <Allowances<T>>::insert(1474 collection.id,1475 (item_id, from.as_sub(), sender.as_sub()),1476 allowance,1477 );1478 } else {1479 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1480 }14811482 match collection.mode {1483 CollectionMode::NFT => {1484 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1485 }1486 CollectionMode::Fungible(_) => {1487 Self::transfer_fungible(collection, amount, from, recipient)?1488 }1489 CollectionMode::ReFungible => Self::transfer_refungible(1490 collection,1491 item_id,1492 amount,1493 from.clone(),1494 recipient.clone(),1495 )?,1496 _ => (),1497 };14981499 if matches!(collection.mode, CollectionMode::Fungible(_)) {1500 collection.log(ERC20Events::Approval {1501 owner: *from.as_eth(),1502 spender: *sender.as_eth(),1503 value: allowance.into(),1504 })?;1505 }15061507 Ok(())1508 }15091510 pub fn set_variable_meta_data_internal(1511 sender: &T::CrossAccountId,1512 collection: &CollectionHandle<T>,1513 item_id: TokenId,1514 data: Vec<u8>,1515 ) -> DispatchResult {1516 Self::token_exists(collection, item_id)?;15171518 ensure!(1519 CUSTOM_DATA_LIMIT >= data.len() as u32,1520 Error::<T>::TokenVariableDataLimitExceeded1521 );15221523 1524 ensure!(1525 Self::is_item_owner(sender, collection, item_id)?1526 || Self::is_owner_or_admin_permissions(collection, sender)?,1527 Error::<T>::NoPermission1528 );15291530 match collection.mode {1531 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1532 CollectionMode::ReFungible => {1533 Self::set_re_fungible_variable_data(collection, item_id, data)?1534 }1535 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1536 _ => fail!(Error::<T>::UnexpectedCollectionType),1537 };15381539 Ok(())1540 }15411542 pub fn meta_update_check(1543 sender: &T::CrossAccountId,1544 collection: &CollectionHandle<T>,1545 item_id: TokenId,1546 ) -> DispatchResult {1547 match collection.meta_update_permission {1548 MetaUpdatePermission::ItemOwner => ensure!(1549 Self::is_item_owner(sender, collection, item_id)?,1550 Error::<T>::NoPermission1551 ),1552 MetaUpdatePermission::Admin => ensure!(1553 Self::is_owner_or_admin_permissions(collection, sender)?,1554 Error::<T>::NoPermission1555 ),1556 MetaUpdatePermission::None => fail!(Error::<T>::MetadataUpdateDenied),1557 }15581559 Ok(())1560 }15611562 pub fn get_variable_metadata(1563 collection: &CollectionHandle<T>,1564 item_id: TokenId,1565 ) -> Result<Vec<u8>, DispatchError> {1566 Ok(match collection.mode {1567 CollectionMode::NFT => {1568 <NftItemList<T>>::get(collection.id, item_id)1569 .ok_or(Error::<T>::TokenNotFound)?1570 .variable_data1571 }1572 CollectionMode::ReFungible => {1573 <ReFungibleItemList<T>>::get(collection.id, item_id)1574 .ok_or(Error::<T>::TokenNotFound)?1575 .variable_data1576 }1577 _ => fail!(Error::<T>::UnexpectedCollectionType),1578 })1579 }15801581 pub fn create_multiple_items_internal(1582 sender: &T::CrossAccountId,1583 collection: &CollectionHandle<T>,1584 owner: &T::CrossAccountId,1585 items_data: Vec<CreateItemData>,1586 ) -> DispatchResult {1587 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;15881589 for data in &items_data {1590 Self::validate_create_item_args(collection, data)?;1591 }1592 for data in &items_data {1593 Self::create_item_no_validation(collection, owner, data.clone())?;1594 }15951596 Ok(())1597 }15981599 pub fn burn_item_internal(1600 sender: &T::CrossAccountId,1601 collection: &CollectionHandle<T>,1602 item_id: TokenId,1603 value: u128,1604 ) -> DispatchResult {1605 ensure!(1606 Self::is_item_owner(sender, collection, item_id)?1607 || (collection.limits.owner_can_transfer1608 && Self::is_owner_or_admin_permissions(collection, sender)?),1609 Error::<T>::NoPermission1610 );16111612 if collection.access == AccessMode::WhiteList {1613 Self::check_white_list(collection, sender)?;1614 }16151616 match collection.mode {1617 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1618 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1619 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1620 _ => (),1621 };16221623 Ok(())1624 }16251626 pub fn toggle_white_list_internal(1627 sender: &T::CrossAccountId,1628 collection: &CollectionHandle<T>,1629 address: &T::CrossAccountId,1630 whitelisted: bool,1631 ) -> DispatchResult {1632 Self::check_owner_or_admin_permissions(collection, sender)?;16331634 if whitelisted {1635 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1636 } else {1637 <WhiteList<T>>::remove(collection.id, address.as_sub());1638 }16391640 Ok(())1641 }16421643 fn is_correct_transfer(1644 collection: &CollectionHandle<T>,1645 recipient: &T::CrossAccountId,1646 ) -> DispatchResult {1647 let collection_id = collection.id;16481649 1650 collection.consume_sload()?;1651 let account_items: u32 =1652 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1653 ensure!(1654 collection.limits.account_token_ownership_limit > account_items,1655 Error::<T>::AccountTokenLimitExceeded1656 );16571658 1659 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);16601661 Ok(())1662 }16631664 fn can_create_items_in_collection(1665 collection: &CollectionHandle<T>,1666 sender: &T::CrossAccountId,1667 owner: &T::CrossAccountId,1668 amount: u32,1669 ) -> DispatchResult {1670 let collection_id = collection.id;16711672 1673 let total_items: u32 = ItemListIndex::get(collection_id)1674 .checked_add(amount)1675 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1676 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1677 as u32)1678 .checked_add(amount)1679 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1680 ensure!(1681 collection.limits.token_limit >= total_items,1682 Error::<T>::CollectionTokenLimitExceeded1683 );1684 ensure!(1685 collection.limits.account_token_ownership_limit >= account_items,1686 Error::<T>::AccountTokenLimitExceeded1687 );16881689 if !Self::is_owner_or_admin_permissions(collection, sender)? {1690 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1691 Self::check_white_list(collection, owner)?;1692 Self::check_white_list(collection, sender)?;1693 }16941695 Ok(())1696 }16971698 fn validate_create_item_args(1699 target_collection: &CollectionHandle<T>,1700 data: &CreateItemData,1701 ) -> DispatchResult {1702 match target_collection.mode {1703 CollectionMode::NFT => {1704 if !matches!(data, CreateItemData::NFT(_)) {1705 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1706 }1707 }1708 CollectionMode::Fungible(_) => {1709 if !matches!(data, CreateItemData::Fungible(_)) {1710 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1711 }1712 }1713 CollectionMode::ReFungible => {1714 if let CreateItemData::ReFungible(data) = data {1715 1716 ensure!(1717 data.pieces <= MAX_REFUNGIBLE_PIECES,1718 Error::<T>::WrongRefungiblePieces1719 );1720 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1721 } else {1722 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1723 }1724 }1725 _ => {1726 fail!(Error::<T>::UnexpectedCollectionType);1727 }1728 };17291730 Ok(())1731 }17321733 fn create_item_no_validation(1734 collection: &CollectionHandle<T>,1735 owner: &T::CrossAccountId,1736 data: CreateItemData,1737 ) -> DispatchResult {1738 match data {1739 CreateItemData::NFT(data) => {1740 let item = NftItemType {1741 owner: owner.clone(),1742 const_data: data.const_data.into_inner(),1743 variable_data: data.variable_data.into_inner(),1744 };17451746 Self::add_nft_item(collection, item)?;1747 }1748 CreateItemData::Fungible(data) => {1749 Self::add_fungible_item(collection, owner, data.value)?;1750 }1751 CreateItemData::ReFungible(data) => {1752 let owner_list = vec![Ownership {1753 owner: owner.clone(),1754 fraction: data.pieces,1755 }];17561757 let item = ReFungibleItemType {1758 owner: owner_list,1759 const_data: data.const_data.into_inner(),1760 variable_data: data.variable_data.into_inner(),1761 };17621763 Self::add_refungible_item(collection, item)?;1764 }1765 };17661767 Ok(())1768 }17691770 fn add_fungible_item(1771 collection: &CollectionHandle<T>,1772 owner: &T::CrossAccountId,1773 value: u128,1774 ) -> DispatchResult {1775 let collection_id = collection.id;17761777 1778 collection.consume_sload()?;1779 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17801781 1782 let item = FungibleItemType {1783 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1784 };1785 collection.consume_sstore()?;1786 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17871788 1789 collection.consume_sload()?;1790 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1791 .checked_add(value)1792 .ok_or(Error::<T>::NumOverflow)?;1793 collection.consume_sstore()?;1794 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17951796 collection.log(ERC20Events::Transfer {1797 from: H160::default(),1798 to: *owner.as_eth(),1799 value: value.into(),1800 })?;1801 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1802 Ok(())1803 }18041805 fn add_refungible_item(1806 collection: &CollectionHandle<T>,1807 item: ReFungibleItemType<T::CrossAccountId>,1808 ) -> DispatchResult {1809 let collection_id = collection.id;18101811 let current_index = <ItemListIndex>::get(collection_id)1812 .checked_add(1)1813 .ok_or(Error::<T>::NumOverflow)?;1814 let itemcopy = item.clone();18151816 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1817 let item_owner = item.owner.first().expect("only one owner is defined");18181819 let value = item_owner.fraction;1820 let owner = item_owner.owner.clone();18211822 Self::add_token_index(collection, current_index, &owner)?;18231824 <ItemListIndex>::insert(collection_id, current_index);1825 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18261827 1828 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1829 .checked_add(value)1830 .ok_or(Error::<T>::NumOverflow)?;1831 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18321833 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1834 Ok(())1835 }18361837 fn add_nft_item(1838 collection: &CollectionHandle<T>,1839 item: NftItemType<T::CrossAccountId>,1840 ) -> DispatchResult {1841 let collection_id = collection.id;18421843 let current_index = <ItemListIndex>::get(collection_id)1844 .checked_add(1)1845 .ok_or(Error::<T>::NumOverflow)?;18461847 let item_owner = item.owner.clone();1848 Self::add_token_index(collection, current_index, &item.owner)?;18491850 <ItemListIndex>::insert(collection_id, current_index);1851 <NftItemList<T>>::insert(collection_id, current_index, item);18521853 1854 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1855 .checked_add(1)1856 .ok_or(Error::<T>::NumOverflow)?;1857 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18581859 collection.log(ERC721Events::Transfer {1860 from: H160::default(),1861 to: *item_owner.as_eth(),1862 token_id: current_index.into(),1863 })?;1864 Self::deposit_event(RawEvent::ItemCreated(1865 collection_id,1866 current_index,1867 item_owner,1868 ));1869 Ok(())1870 }18711872 fn burn_refungible_item(1873 collection: &CollectionHandle<T>,1874 item_id: TokenId,1875 owner: &T::CrossAccountId,1876 ) -> DispatchResult {1877 let collection_id = collection.id;18781879 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1880 .ok_or(Error::<T>::TokenNotFound)?;1881 let rft_balance = token1882 .owner1883 .iter()1884 .find(|&i| i.owner == *owner)1885 .ok_or(Error::<T>::TokenNotFound)?;1886 Self::remove_token_index(collection, item_id, owner)?;18871888 1889 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1890 .checked_sub(rft_balance.fraction)1891 .ok_or(Error::<T>::NumOverflow)?;1892 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18931894 1895 let index = token1896 .owner1897 .iter()1898 .position(|i| i.owner == *owner)1899 .expect("owned item is exists");1900 token.owner.remove(index);1901 let owner_count = token.owner.len();19021903 1904 if owner_count == 0 {1905 <ReFungibleItemList<T>>::remove(collection_id, item_id);1906 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1907 } else {1908 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1909 }19101911 Ok(())1912 }19131914 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1915 let collection_id = collection.id;19161917 let item =1918 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1919 Self::remove_token_index(collection, item_id, &item.owner)?;19201921 1922 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1923 .checked_sub(1)1924 .ok_or(Error::<T>::NumOverflow)?;1925 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1926 <NftItemList<T>>::remove(collection_id, item_id);1927 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);19281929 collection.log(ERC721Events::Transfer {1930 from: *item.owner.as_eth(),1931 to: H160::default(),1932 token_id: item_id.into(),1933 })?;1934 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1935 Ok(())1936 }19371938 fn burn_fungible_item(1939 owner: &T::CrossAccountId,1940 collection: &CollectionHandle<T>,1941 value: u128,1942 ) -> DispatchResult {1943 let collection_id = collection.id;19441945 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1946 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19471948 1949 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1950 .checked_sub(value)1951 .ok_or(Error::<T>::NumOverflow)?;1952 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19531954 if balance.value - value > 0 {1955 balance.value -= value;1956 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1957 } else {1958 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1959 }19601961 collection.log(ERC20Events::Transfer {1962 from: *owner.as_eth(),1963 to: H160::default(),1964 value: value.into(),1965 })?;1966 Ok(())1967 }19681969 pub fn get_collection(1970 collection_id: CollectionId,1971 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1972 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1973 }19741975 fn check_owner_permissions(1976 target_collection: &CollectionHandle<T>,1977 subject: &T::AccountId,1978 ) -> DispatchResult {1979 ensure!(1980 *subject == target_collection.owner,1981 Error::<T>::NoPermission1982 );19831984 Ok(())1985 }19861987 fn is_owner_or_admin_permissions(1988 collection: &CollectionHandle<T>,1989 subject: &T::CrossAccountId,1990 ) -> Result<bool, DispatchError> {1991 collection.consume_sload()?;1992 Ok(*subject.as_sub() == collection.owner1993 || <AdminList<T>>::get(collection.id).contains(subject))1994 }19951996 fn check_owner_or_admin_permissions(1997 collection: &CollectionHandle<T>,1998 subject: &T::CrossAccountId,1999 ) -> DispatchResult {2000 ensure!(2001 Self::is_owner_or_admin_permissions(collection, subject)?,2002 Error::<T>::NoPermission2003 );20042005 Ok(())2006 }20072008 fn owned_amount(2009 subject: &T::CrossAccountId,2010 collection: &CollectionHandle<T>,2011 item_id: TokenId,2012 ) -> Result<Option<u128>, DispatchError> {2013 collection.consume_sload()?;2014 Ok(Self::owned_amount_unchecked(subject, collection, item_id))2015 }20162017 fn owned_amount_unchecked(2018 subject: &T::CrossAccountId,2019 target_collection: &CollectionHandle<T>,2020 item_id: TokenId,2021 ) -> Option<u128> {2022 let collection_id = target_collection.id;20232024 match target_collection.mode {2025 CollectionMode::NFT => {2026 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)2027 }2028 CollectionMode::Fungible(_) => {2029 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)2030 }2031 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2032 .owner2033 .iter()2034 .find(|i| i.owner == *subject)2035 .map(|i| i.fraction),2036 CollectionMode::Invalid => None,2037 }2038 }20392040 fn is_item_owner(2041 subject: &T::CrossAccountId,2042 target_collection: &CollectionHandle<T>,2043 item_id: TokenId,2044 ) -> Result<bool, DispatchError> {2045 Ok(match target_collection.mode {2046 CollectionMode::Fungible(_) => true,2047 _ => Self::owned_amount(subject, target_collection, item_id)?.is_some(),2048 })2049 }20502051 fn check_white_list(2052 collection: &CollectionHandle<T>,2053 address: &T::CrossAccountId,2054 ) -> DispatchResult {2055 collection.consume_sload()?;2056 ensure!(2057 <WhiteList<T>>::contains_key(collection.id, address.as_sub()),2058 Error::<T>::AddresNotInWhiteList,2059 );2060 Ok(())2061 }20622063 2064 2065 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2066 let collection_id = target_collection.id;2067 let exists = match target_collection.mode {2068 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2069 CollectionMode::Fungible(_) => true,2070 CollectionMode::ReFungible => {2071 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)2072 }2073 _ => false,2074 };20752076 ensure!(exists, Error::<T>::TokenNotFound);2077 Ok(())2078 }20792080 fn transfer_fungible(2081 collection: &CollectionHandle<T>,2082 value: u128,2083 owner: &T::CrossAccountId,2084 recipient: &T::CrossAccountId,2085 ) -> DispatchResult {2086 let collection_id = collection.id;20872088 collection.consume_sload()?;2089 collection.consume_sload()?;2090 let mut recipient_balance = <FungibleItemList<T>>::get(collection_id, recipient.as_sub());2091 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());20922093 recipient_balance.value = recipient_balance2094 .value2095 .checked_add(value)2096 .ok_or(Error::<T>::NumOverflow)?;2097 balance.value = balance2098 .value2099 .checked_sub(value)2100 .ok_or(Error::<T>::TokenValueTooLow)?;21012102 2103 collection.consume_sstore()?;2104 collection.consume_sstore()?;2105 if balance.value != 0 {2106 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value);2107 } else {2108 <Balance<T>>::remove(collection_id, owner.as_sub());2109 }2110 <Balance<T>>::insert(collection_id, recipient.as_sub(), recipient_balance.value);21112112 2113 collection.consume_sstore()?;2114 collection.consume_sstore()?;2115 if balance.value != 0 {2116 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2117 } else {2118 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2119 }2120 <FungibleItemList<T>>::insert(collection_id, recipient.as_sub(), recipient_balance);21212122 collection.log(ERC20Events::Transfer {2123 from: *owner.as_eth(),2124 to: *recipient.as_eth(),2125 value: value.into(),2126 })?;2127 Self::deposit_event(RawEvent::Transfer(2128 collection.id,2129 1,2130 owner.clone(),2131 recipient.clone(),2132 value,2133 ));21342135 Ok(())2136 }21372138 fn transfer_refungible(2139 collection: &CollectionHandle<T>,2140 item_id: TokenId,2141 value: u128,2142 owner: T::CrossAccountId,2143 new_owner: T::CrossAccountId,2144 ) -> DispatchResult {2145 let collection_id = collection.id;2146 collection.consume_sload()?;2147 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2148 .ok_or(Error::<T>::TokenNotFound)?;21492150 let item = full_item2151 .owner2152 .iter()2153 .find(|i| i.owner == owner)2154 .ok_or(Error::<T>::TokenNotFound)?;2155 let amount = item.fraction;21562157 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21582159 collection.consume_sload()?;2160 2161 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2162 .checked_sub(value)2163 .ok_or(Error::<T>::NumOverflow)?;2164 collection.consume_sstore()?;2165 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21662167 collection.consume_sload()?;2168 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2169 .checked_add(value)2170 .ok_or(Error::<T>::NumOverflow)?;2171 collection.consume_sstore()?;2172 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21732174 let old_owner = item.owner.clone();2175 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21762177 let mut new_full_item = full_item.clone();2178 2179 if amount == value && !new_owner_has_account {2180 2181 2182 new_full_item2183 .owner2184 .iter_mut()2185 .find(|i| i.owner == owner)2186 .expect("old owner does present in refungible")2187 .owner = new_owner.clone();2188 collection.consume_sstore()?;2189 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21902191 2192 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;2193 } else {2194 new_full_item2195 .owner2196 .iter_mut()2197 .find(|i| i.owner == owner)2198 .expect("old owner does present in refungible")2199 .fraction -= value;22002201 2202 if new_owner_has_account {2203 2204 new_full_item2205 .owner2206 .iter_mut()2207 .find(|i| i.owner == new_owner)2208 .expect("new owner has account")2209 .fraction += value;2210 } else {2211 2212 new_full_item.owner.push(Ownership {2213 owner: new_owner.clone(),2214 fraction: value,2215 });2216 Self::add_token_index(collection, item_id, &new_owner)?;2217 }22182219 collection.consume_sstore()?;2220 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2221 }22222223 Self::deposit_event(RawEvent::Transfer(2224 collection.id,2225 item_id,2226 owner,2227 new_owner,2228 amount,2229 ));22302231 Ok(())2232 }22332234 fn transfer_nft(2235 collection: &CollectionHandle<T>,2236 item_id: TokenId,2237 sender: T::CrossAccountId,2238 new_owner: T::CrossAccountId,2239 ) -> DispatchResult {2240 let collection_id = collection.id;2241 collection.consume_sload()?;2242 let mut item =2243 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;22442245 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);22462247 collection.consume_sload()?;2248 2249 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2250 .checked_sub(1)2251 .ok_or(Error::<T>::NumOverflow)?;2252 collection.consume_sstore()?;2253 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);22542255 collection.consume_sload()?;2256 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2257 .checked_add(1)2258 .ok_or(Error::<T>::NumOverflow)?;2259 collection.consume_sstore()?;2260 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);22612262 2263 let old_owner = item.owner.clone();2264 item.owner = new_owner.clone();2265 collection.consume_sstore()?;2266 <NftItemList<T>>::insert(collection_id, item_id, item);22672268 2269 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;22702271 collection.log(ERC721Events::Transfer {2272 from: *sender.as_eth(),2273 to: *new_owner.as_eth(),2274 token_id: item_id.into(),2275 })?;2276 Self::deposit_event(RawEvent::Transfer(2277 collection.id,2278 item_id,2279 sender,2280 new_owner,2281 1,2282 ));22832284 Ok(())2285 }22862287 fn set_re_fungible_variable_data(2288 collection: &CollectionHandle<T>,2289 item_id: TokenId,2290 data: Vec<u8>,2291 ) -> DispatchResult {2292 let collection_id = collection.id;2293 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2294 .ok_or(Error::<T>::TokenNotFound)?;22952296 item.variable_data = data;22972298 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22992300 Ok(())2301 }23022303 fn set_nft_variable_data(2304 collection: &CollectionHandle<T>,2305 item_id: TokenId,2306 data: Vec<u8>,2307 ) -> DispatchResult {2308 let collection_id = collection.id;2309 let mut item =2310 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;23112312 item.variable_data = data;23132314 <NftItemList<T>>::insert(collection_id, item_id, item);23152316 Ok(())2317 }23182319 #[allow(dead_code)]2320 fn init_collection(item: &Collection<T>) {2321 2322 assert!(2323 item.decimal_points <= MAX_DECIMAL_POINTS,2324 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2325 );2326 assert!(2327 item.name.len() <= 64,2328 "Collection name can not be longer than 63 char"2329 );2330 assert!(2331 item.name.len() <= 256,2332 "Collection description can not be longer than 255 char"2333 );2334 assert!(2335 item.token_prefix.len() <= 16,2336 "Token prefix can not be longer than 15 char"2337 );23382339 2340 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();23412342 CreatedCollectionCount::put(next_id);2343 }23442345 #[allow(dead_code)]2346 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2347 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23482349 Self::add_token_index(2350 &CollectionHandle::get(collection_id).unwrap(),2351 current_index,2352 &item.owner,2353 )2354 .unwrap();23552356 <ItemListIndex>::insert(collection_id, current_index);23572358 2359 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2360 .checked_add(1)2361 .unwrap();2362 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2363 }23642365 #[allow(dead_code)]2366 fn init_fungible_token(2367 collection_id: CollectionId,2368 owner: &T::CrossAccountId,2369 item: &FungibleItemType,2370 ) {2371 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23722373 Self::add_token_index(2374 &CollectionHandle::get(collection_id).unwrap(),2375 current_index,2376 owner,2377 )2378 .unwrap();23792380 <ItemListIndex>::insert(collection_id, current_index);23812382 2383 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2384 .checked_add(item.value)2385 .unwrap();2386 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2387 }23882389 #[allow(dead_code)]2390 fn init_refungible_token(2391 collection_id: CollectionId,2392 item: &ReFungibleItemType<T::CrossAccountId>,2393 ) {2394 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23952396 let value = item.owner.first().unwrap().fraction;2397 let owner = item.owner.first().unwrap().owner.clone();23982399 Self::add_token_index(2400 &CollectionHandle::get(collection_id).unwrap(),2401 current_index,2402 &owner,2403 )2404 .unwrap();24052406 <ItemListIndex>::insert(collection_id, current_index);24072408 2409 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2410 .checked_add(value)2411 .unwrap();2412 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2413 }24142415 fn add_token_index(2416 collection: &CollectionHandle<T>,2417 item_index: TokenId,2418 owner: &T::CrossAccountId,2419 ) -> DispatchResult {2420 2421 collection.consume_sload()?;2422 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2423 2424 collection.consume_sload()?;2425 let count = <AccountItemCount<T>>::get(owner.as_sub());2426 ensure!(2427 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2428 Error::<T>::AddressOwnershipLimitExceeded2429 );24302431 collection.consume_sstore()?;2432 <AccountItemCount<T>>::insert(2433 owner.as_sub(),2434 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2435 );2436 } else {2437 collection.consume_sstore()?;2438 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2439 }24402441 collection.consume_sload()?;2442 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2443 if list_exists {2444 collection.consume_sload()?;2445 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2446 let item_contains = list.contains(&item_index.clone());24472448 if !item_contains {2449 list.push(item_index);2450 }24512452 collection.consume_sstore()?;2453 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2454 } else {2455 let itm = vec![item_index];2456 collection.consume_sstore()?;2457 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), itm);2458 }24592460 Ok(())2461 }24622463 fn remove_token_index(2464 collection: &CollectionHandle<T>,2465 item_index: TokenId,2466 owner: &T::CrossAccountId,2467 ) -> DispatchResult {2468 2469 collection.consume_sload()?;2470 collection.consume_sstore()?;2471 <AccountItemCount<T>>::insert(2472 owner.as_sub(),2473 <AccountItemCount<T>>::get(owner.as_sub())2474 .checked_sub(1)2475 .ok_or(Error::<T>::NumOverflow)?,2476 );24772478 collection.consume_sload()?;2479 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2480 if list_exists {2481 collection.consume_sload()?;2482 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2483 let item_contains = list.contains(&item_index.clone());24842485 if item_contains {2486 list.retain(|&item| item != item_index);2487 collection.consume_sstore()?;2488 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2489 }2490 }24912492 Ok(())2493 }24942495 fn move_token_index(2496 collection: &CollectionHandle<T>,2497 item_index: TokenId,2498 old_owner: &T::CrossAccountId,2499 new_owner: &T::CrossAccountId,2500 ) -> DispatchResult {2501 Self::remove_token_index(collection, item_index, old_owner)?;2502 Self::add_token_index(collection, item_index, new_owner)?;25032504 Ok(())2505 }2506}25072508sp_api::decl_runtime_apis! {2509 pub trait NftApi {2510 2511 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2512 }2513}