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;974 <CollectionById<T>>::insert(target_collection.id, target_collection.collection);975976 Ok(())977 }978979 980 981 982 983 984 985 986 987 988 989 990 991 992 #[weight = <SelfWeightOf<T>>::burn_item()]993 #[transactional]994 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {995996 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);997 let target_collection = Self::get_collection(collection_id)?;998999 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;10001001 target_collection.submit_logs()1002 }10031004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 #[weight = <SelfWeightOf<T>>::transfer()]1028 #[transactional]1029 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1030 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1031 let collection = Self::get_collection(collection_id)?;10321033 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;10341035 collection.submit_logs()1036 }10371038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 #[weight = <SelfWeightOf<T>>::approve()]1054 #[transactional]1055 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1056 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1057 let collection = Self::get_collection(collection_id)?;10581059 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10601061 collection.submit_logs()1062 }10631064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 #[weight = <SelfWeightOf<T>>::transfer_from()]1084 #[transactional]1085 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1086 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1087 let collection = Self::get_collection(collection_id)?;10881089 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10901091 collection.submit_logs()1092 }1093 1094 1095 1096 1097 10981099 11001101 11021103 1104 11051106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1119 #[transactional]1120 pub fn set_variable_meta_data (1121 origin,1122 collection_id: CollectionId,1123 item_id: TokenId,1124 data: Vec<u8>1125 ) -> DispatchResult {1126 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11271128 let collection = Self::get_collection(collection_id)?;11291130 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;11311132 Ok(())1133 }11341135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 #[weight = <SelfWeightOf<T>>::set_schema_version()]1150 #[transactional]1151 pub fn set_schema_version(1152 origin,1153 collection_id: CollectionId,1154 version: SchemaVersion1155 ) -> DispatchResult {1156 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1157 let mut target_collection = Self::get_collection(collection_id)?;1158 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1159 target_collection.schema_version = version;1160 target_collection.save()1161 }11621163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1176 #[transactional]1177 pub fn set_offchain_schema(1178 origin,1179 collection_id: CollectionId,1180 schema: Vec<u8>1181 ) -> DispatchResult {1182 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1183 let mut target_collection = Self::get_collection(collection_id)?;1184 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11851186 1187 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11881189 target_collection.offchain_schema = schema;1190 target_collection.save()1191 }11921193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1206 #[transactional]1207 pub fn set_const_on_chain_schema (1208 origin,1209 collection_id: CollectionId,1210 schema: Vec<u8>1211 ) -> DispatchResult {1212 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1213 let mut target_collection = Self::get_collection(collection_id)?;1214 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12151216 1217 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");12181219 target_collection.const_on_chain_schema = schema;1220 target_collection.save()1221 }12221223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1236 #[transactional]1237 pub fn set_variable_on_chain_schema (1238 origin,1239 collection_id: CollectionId,1240 schema: Vec<u8>1241 ) -> DispatchResult {1242 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1243 let mut target_collection = Self::get_collection(collection_id)?;1244 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12451246 1247 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12481249 target_collection.variable_on_chain_schema = schema;1250 target_collection.save()1251 }12521253 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1254 #[transactional]1255 pub fn set_collection_limits(1256 origin,1257 collection_id: u32,1258 new_limits: CollectionLimits<T::BlockNumber>,1259 ) -> DispatchResult {1260 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1261 let mut target_collection = Self::get_collection(collection_id)?;1262 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1263 let old_limits = &target_collection.limits;12641265 1266 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1267 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1268 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1269 Error::<T>::CollectionLimitBoundsExceeded);12701271 1272 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1273 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12741275 ensure!(1276 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1277 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1278 Error::<T>::OwnerPermissionsCantBeReverted,1279 );12801281 target_collection.limits = new_limits;12821283 target_collection.save()1284 }1285 }1286}12871288impl<T: Config> Module<T> {1289 pub fn create_item_internal(1290 sender: &T::CrossAccountId,1291 collection: &CollectionHandle<T>,1292 owner: &T::CrossAccountId,1293 data: CreateItemData,1294 ) -> DispatchResult {1295 ensure!(1296 owner != &T::CrossAccountId::from_eth(H160([0; 20])),1297 Error::<T>::AddressIsZero1298 );12991300 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1301 Self::validate_create_item_args(collection, &data)?;1302 Self::create_item_no_validation(collection, owner, data)?;13031304 Ok(())1305 }13061307 pub fn transfer_internal(1308 sender: &T::CrossAccountId,1309 recipient: &T::CrossAccountId,1310 target_collection: &CollectionHandle<T>,1311 item_id: TokenId,1312 value: u128,1313 ) -> DispatchResult {1314 ensure!(1315 recipient != &T::CrossAccountId::from_eth(H160([0; 20])),1316 Error::<T>::AddressIsZero1317 );13181319 1320 Self::is_correct_transfer(target_collection, recipient)?;13211322 1323 ensure!(1324 Self::is_item_owner(sender, target_collection, item_id)?1325 || Self::is_owner_or_admin_permissions(target_collection, sender)?,1326 Error::<T>::NoPermission1327 );13281329 if target_collection.access == AccessMode::WhiteList {1330 Self::check_white_list(target_collection, sender)?;1331 Self::check_white_list(target_collection, recipient)?;1332 }13331334 match target_collection.mode {1335 CollectionMode::NFT => Self::transfer_nft(1336 target_collection,1337 item_id,1338 sender.clone(),1339 recipient.clone(),1340 )?,1341 CollectionMode::Fungible(_) => {1342 Self::transfer_fungible(target_collection, value, sender, recipient)?1343 }1344 CollectionMode::ReFungible => Self::transfer_refungible(1345 target_collection,1346 item_id,1347 value,1348 sender.clone(),1349 recipient.clone(),1350 )?,1351 _ => (),1352 };13531354 Self::deposit_event(RawEvent::Transfer(1355 target_collection.id,1356 item_id,1357 sender.clone(),1358 recipient.clone(),1359 value,1360 ));13611362 Ok(())1363 }13641365 pub fn approve_internal(1366 sender: &T::CrossAccountId,1367 spender: &T::CrossAccountId,1368 collection: &CollectionHandle<T>,1369 item_id: TokenId,1370 amount: u128,1371 ) -> DispatchResult {1372 Self::token_exists(collection, item_id)?;13731374 1375 let bypasses_limits = collection.limits.owner_can_transfer1376 && Self::is_owner_or_admin_permissions(collection, sender)?;13771378 let allowance_limit = if bypasses_limits {1379 None1380 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id)? {1381 Some(amount)1382 } else {1383 fail!(Error::<T>::NoPermission);1384 };13851386 if collection.access == AccessMode::WhiteList {1387 Self::check_white_list(collection, sender)?;1388 Self::check_white_list(collection, spender)?;1389 }13901391 collection.consume_sload()?;1392 let allowance: u128 = amount1393 .checked_add(<Allowances<T>>::get(1394 collection.id,1395 (item_id, sender.as_sub(), spender.as_sub()),1396 ))1397 .ok_or(Error::<T>::NumOverflow)?;1398 if let Some(limit) = allowance_limit {1399 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1400 }1401 collection.consume_sstore()?;1402 <Allowances<T>>::insert(1403 collection.id,1404 (item_id, sender.as_sub(), spender.as_sub()),1405 allowance,1406 );14071408 if matches!(collection.mode, CollectionMode::NFT) {1409 1410 collection.log(ERC721Events::Approval {1411 owner: *sender.as_eth(),1412 approved: *spender.as_eth(),1413 token_id: item_id.into(),1414 })?;1415 }14161417 if matches!(collection.mode, CollectionMode::Fungible(_)) {1418 1419 collection.log(ERC20Events::Approval {1420 owner: *sender.as_eth(),1421 spender: *spender.as_eth(),1422 value: allowance.into(),1423 })?;1424 }14251426 Self::deposit_event(RawEvent::Approved(1427 collection.id,1428 item_id,1429 sender.clone(),1430 spender.clone(),1431 allowance,1432 ));1433 Ok(())1434 }14351436 pub fn transfer_from_internal(1437 sender: &T::CrossAccountId,1438 from: &T::CrossAccountId,1439 recipient: &T::CrossAccountId,1440 collection: &CollectionHandle<T>,1441 item_id: TokenId,1442 amount: u128,1443 ) -> DispatchResult {1444 if sender == from {1445 1446 return Self::transfer_internal(from, recipient, collection, item_id, amount);1447 }14481449 1450 collection.consume_sload()?;1451 let approval: u128 =1452 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));14531454 1455 Self::is_correct_transfer(collection, recipient)?;14561457 1458 ensure!(1459 approval >= amount1460 || (collection.limits.owner_can_transfer1461 && Self::is_owner_or_admin_permissions(collection, sender)?),1462 Error::<T>::NoPermission1463 );14641465 if collection.access == AccessMode::WhiteList {1466 Self::check_white_list(collection, sender)?;1467 Self::check_white_list(collection, recipient)?;1468 }14691470 1471 let allowance = approval.saturating_sub(amount);1472 collection.consume_sstore()?;1473 if allowance > 0 {1474 <Allowances<T>>::insert(1475 collection.id,1476 (item_id, from.as_sub(), sender.as_sub()),1477 allowance,1478 );1479 } else {1480 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1481 }14821483 match collection.mode {1484 CollectionMode::NFT => {1485 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1486 }1487 CollectionMode::Fungible(_) => {1488 Self::transfer_fungible(collection, amount, from, recipient)?1489 }1490 CollectionMode::ReFungible => Self::transfer_refungible(1491 collection,1492 item_id,1493 amount,1494 from.clone(),1495 recipient.clone(),1496 )?,1497 _ => (),1498 };14991500 if matches!(collection.mode, CollectionMode::Fungible(_)) {1501 collection.log(ERC20Events::Approval {1502 owner: *from.as_eth(),1503 spender: *sender.as_eth(),1504 value: allowance.into(),1505 })?;1506 }15071508 Ok(())1509 }15101511 pub fn set_variable_meta_data_internal(1512 sender: &T::CrossAccountId,1513 collection: &CollectionHandle<T>,1514 item_id: TokenId,1515 data: Vec<u8>,1516 ) -> DispatchResult {1517 Self::token_exists(collection, item_id)?;15181519 ensure!(1520 CUSTOM_DATA_LIMIT >= data.len() as u32,1521 Error::<T>::TokenVariableDataLimitExceeded1522 );15231524 1525 ensure!(1526 Self::is_item_owner(sender, collection, item_id)?1527 || Self::is_owner_or_admin_permissions(collection, sender)?,1528 Error::<T>::NoPermission1529 );15301531 match collection.mode {1532 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1533 CollectionMode::ReFungible => {1534 Self::set_re_fungible_variable_data(collection, item_id, data)?1535 }1536 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1537 _ => fail!(Error::<T>::UnexpectedCollectionType),1538 };15391540 Ok(())1541 }15421543 pub fn meta_update_check(1544 sender: &T::CrossAccountId,1545 collection: &CollectionHandle<T>,1546 item_id: TokenId,1547 ) -> DispatchResult {1548 match collection.meta_update_permission {1549 MetaUpdatePermission::ItemOwner => ensure!(1550 Self::is_item_owner(sender, collection, item_id)?,1551 Error::<T>::NoPermission1552 ),1553 MetaUpdatePermission::Admin => ensure!(1554 Self::is_owner_or_admin_permissions(collection, sender)?,1555 Error::<T>::NoPermission1556 ),1557 MetaUpdatePermission::None => fail!(Error::<T>::MetadataUpdateDenied),1558 }15591560 Ok(())1561 }15621563 pub fn create_multiple_items_internal(1564 sender: &T::CrossAccountId,1565 collection: &CollectionHandle<T>,1566 owner: &T::CrossAccountId,1567 items_data: Vec<CreateItemData>,1568 ) -> DispatchResult {1569 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;15701571 for data in &items_data {1572 Self::validate_create_item_args(collection, data)?;1573 }1574 for data in &items_data {1575 Self::create_item_no_validation(collection, owner, data.clone())?;1576 }15771578 Ok(())1579 }15801581 pub fn burn_item_internal(1582 sender: &T::CrossAccountId,1583 collection: &CollectionHandle<T>,1584 item_id: TokenId,1585 value: u128,1586 ) -> DispatchResult {1587 ensure!(1588 Self::is_item_owner(sender, collection, item_id)?1589 || (collection.limits.owner_can_transfer1590 && Self::is_owner_or_admin_permissions(collection, sender)?),1591 Error::<T>::NoPermission1592 );15931594 if collection.access == AccessMode::WhiteList {1595 Self::check_white_list(collection, sender)?;1596 }15971598 match collection.mode {1599 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1600 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1601 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1602 _ => (),1603 };16041605 Ok(())1606 }16071608 pub fn toggle_white_list_internal(1609 sender: &T::CrossAccountId,1610 collection: &CollectionHandle<T>,1611 address: &T::CrossAccountId,1612 whitelisted: bool,1613 ) -> DispatchResult {1614 Self::check_owner_or_admin_permissions(collection, sender)?;16151616 if whitelisted {1617 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1618 } else {1619 <WhiteList<T>>::remove(collection.id, address.as_sub());1620 }16211622 Ok(())1623 }16241625 fn is_correct_transfer(1626 collection: &CollectionHandle<T>,1627 recipient: &T::CrossAccountId,1628 ) -> DispatchResult {1629 let collection_id = collection.id;16301631 1632 collection.consume_sload()?;1633 let account_items: u32 =1634 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1635 ensure!(1636 collection.limits.account_token_ownership_limit > account_items,1637 Error::<T>::AccountTokenLimitExceeded1638 );16391640 1641 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);16421643 Ok(())1644 }16451646 fn can_create_items_in_collection(1647 collection: &CollectionHandle<T>,1648 sender: &T::CrossAccountId,1649 owner: &T::CrossAccountId,1650 amount: u32,1651 ) -> DispatchResult {1652 let collection_id = collection.id;16531654 1655 let total_items: u32 = ItemListIndex::get(collection_id)1656 .checked_add(amount)1657 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1658 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1659 as u32)1660 .checked_add(amount)1661 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1662 ensure!(1663 collection.limits.token_limit >= total_items,1664 Error::<T>::CollectionTokenLimitExceeded1665 );1666 ensure!(1667 collection.limits.account_token_ownership_limit >= account_items,1668 Error::<T>::AccountTokenLimitExceeded1669 );16701671 if !Self::is_owner_or_admin_permissions(collection, sender)? {1672 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1673 Self::check_white_list(collection, owner)?;1674 Self::check_white_list(collection, sender)?;1675 }16761677 Ok(())1678 }16791680 fn validate_create_item_args(1681 target_collection: &CollectionHandle<T>,1682 data: &CreateItemData,1683 ) -> DispatchResult {1684 match target_collection.mode {1685 CollectionMode::NFT => {1686 if !matches!(data, CreateItemData::NFT(_)) {1687 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1688 }1689 }1690 CollectionMode::Fungible(_) => {1691 if !matches!(data, CreateItemData::Fungible(_)) {1692 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1693 }1694 }1695 CollectionMode::ReFungible => {1696 if let CreateItemData::ReFungible(data) = data {1697 1698 ensure!(1699 data.pieces <= MAX_REFUNGIBLE_PIECES,1700 Error::<T>::WrongRefungiblePieces1701 );1702 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1703 } else {1704 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1705 }1706 }1707 _ => {1708 fail!(Error::<T>::UnexpectedCollectionType);1709 }1710 };17111712 Ok(())1713 }17141715 fn create_item_no_validation(1716 collection: &CollectionHandle<T>,1717 owner: &T::CrossAccountId,1718 data: CreateItemData,1719 ) -> DispatchResult {1720 match data {1721 CreateItemData::NFT(data) => {1722 let item = NftItemType {1723 owner: owner.clone(),1724 const_data: data.const_data.into_inner(),1725 variable_data: data.variable_data.into_inner(),1726 };17271728 Self::add_nft_item(collection, item)?;1729 }1730 CreateItemData::Fungible(data) => {1731 Self::add_fungible_item(collection, owner, data.value)?;1732 }1733 CreateItemData::ReFungible(data) => {1734 let owner_list = vec![Ownership {1735 owner: owner.clone(),1736 fraction: data.pieces,1737 }];17381739 let item = ReFungibleItemType {1740 owner: owner_list,1741 const_data: data.const_data.into_inner(),1742 variable_data: data.variable_data.into_inner(),1743 };17441745 Self::add_refungible_item(collection, item)?;1746 }1747 };17481749 Ok(())1750 }17511752 fn add_fungible_item(1753 collection: &CollectionHandle<T>,1754 owner: &T::CrossAccountId,1755 value: u128,1756 ) -> DispatchResult {1757 let collection_id = collection.id;17581759 1760 collection.consume_sload()?;1761 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17621763 1764 let item = FungibleItemType {1765 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1766 };1767 collection.consume_sstore()?;1768 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17691770 1771 collection.consume_sload()?;1772 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1773 .checked_add(value)1774 .ok_or(Error::<T>::NumOverflow)?;1775 collection.consume_sstore()?;1776 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17771778 collection.log(ERC20Events::Transfer {1779 from: H160::default(),1780 to: *owner.as_eth(),1781 value: value.into(),1782 })?;1783 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1784 Ok(())1785 }17861787 fn add_refungible_item(1788 collection: &CollectionHandle<T>,1789 item: ReFungibleItemType<T::CrossAccountId>,1790 ) -> DispatchResult {1791 let collection_id = collection.id;17921793 let current_index = <ItemListIndex>::get(collection_id)1794 .checked_add(1)1795 .ok_or(Error::<T>::NumOverflow)?;1796 let itemcopy = item.clone();17971798 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1799 let item_owner = item.owner.first().expect("only one owner is defined");18001801 let value = item_owner.fraction;1802 let owner = item_owner.owner.clone();18031804 Self::add_token_index(collection, current_index, &owner)?;18051806 <ItemListIndex>::insert(collection_id, current_index);1807 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18081809 1810 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1811 .checked_add(value)1812 .ok_or(Error::<T>::NumOverflow)?;1813 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18141815 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1816 Ok(())1817 }18181819 fn add_nft_item(1820 collection: &CollectionHandle<T>,1821 item: NftItemType<T::CrossAccountId>,1822 ) -> DispatchResult {1823 let collection_id = collection.id;18241825 let current_index = <ItemListIndex>::get(collection_id)1826 .checked_add(1)1827 .ok_or(Error::<T>::NumOverflow)?;18281829 let item_owner = item.owner.clone();1830 Self::add_token_index(collection, current_index, &item.owner)?;18311832 <ItemListIndex>::insert(collection_id, current_index);1833 <NftItemList<T>>::insert(collection_id, current_index, item);18341835 1836 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1837 .checked_add(1)1838 .ok_or(Error::<T>::NumOverflow)?;1839 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18401841 collection.log(ERC721Events::Transfer {1842 from: H160::default(),1843 to: *item_owner.as_eth(),1844 token_id: current_index.into(),1845 })?;1846 Self::deposit_event(RawEvent::ItemCreated(1847 collection_id,1848 current_index,1849 item_owner,1850 ));1851 Ok(())1852 }18531854 fn burn_refungible_item(1855 collection: &CollectionHandle<T>,1856 item_id: TokenId,1857 owner: &T::CrossAccountId,1858 ) -> DispatchResult {1859 let collection_id = collection.id;18601861 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1862 .ok_or(Error::<T>::TokenNotFound)?;1863 let rft_balance = token1864 .owner1865 .iter()1866 .find(|&i| i.owner == *owner)1867 .ok_or(Error::<T>::TokenNotFound)?;1868 Self::remove_token_index(collection, item_id, owner)?;18691870 1871 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1872 .checked_sub(rft_balance.fraction)1873 .ok_or(Error::<T>::NumOverflow)?;1874 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18751876 1877 let index = token1878 .owner1879 .iter()1880 .position(|i| i.owner == *owner)1881 .expect("owned item is exists");1882 token.owner.remove(index);1883 let owner_count = token.owner.len();18841885 1886 if owner_count == 0 {1887 <ReFungibleItemList<T>>::remove(collection_id, item_id);1888 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1889 } else {1890 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1891 }18921893 Ok(())1894 }18951896 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1897 let collection_id = collection.id;18981899 let item =1900 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1901 Self::remove_token_index(collection, item_id, &item.owner)?;19021903 1904 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1905 .checked_sub(1)1906 .ok_or(Error::<T>::NumOverflow)?;1907 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1908 <NftItemList<T>>::remove(collection_id, item_id);1909 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);19101911 collection.log(ERC721Events::Transfer {1912 from: *item.owner.as_eth(),1913 to: H160::default(),1914 token_id: item_id.into(),1915 })?;1916 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1917 Ok(())1918 }19191920 fn burn_fungible_item(1921 owner: &T::CrossAccountId,1922 collection: &CollectionHandle<T>,1923 value: u128,1924 ) -> DispatchResult {1925 let collection_id = collection.id;19261927 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1928 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19291930 1931 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1932 .checked_sub(value)1933 .ok_or(Error::<T>::NumOverflow)?;1934 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19351936 if balance.value - value > 0 {1937 balance.value -= value;1938 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1939 } else {1940 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1941 }19421943 collection.log(ERC20Events::Transfer {1944 from: *owner.as_eth(),1945 to: H160::default(),1946 value: value.into(),1947 })?;1948 Ok(())1949 }19501951 pub fn get_collection(1952 collection_id: CollectionId,1953 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1954 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1955 }19561957 fn check_owner_permissions(1958 target_collection: &CollectionHandle<T>,1959 subject: &T::AccountId,1960 ) -> DispatchResult {1961 ensure!(1962 *subject == target_collection.owner,1963 Error::<T>::NoPermission1964 );19651966 Ok(())1967 }19681969 fn is_owner_or_admin_permissions(1970 collection: &CollectionHandle<T>,1971 subject: &T::CrossAccountId,1972 ) -> Result<bool, DispatchError> {1973 collection.consume_sload()?;1974 Ok(*subject.as_sub() == collection.owner1975 || <AdminList<T>>::get(collection.id).contains(subject))1976 }19771978 fn check_owner_or_admin_permissions(1979 collection: &CollectionHandle<T>,1980 subject: &T::CrossAccountId,1981 ) -> DispatchResult {1982 ensure!(1983 Self::is_owner_or_admin_permissions(collection, subject)?,1984 Error::<T>::NoPermission1985 );19861987 Ok(())1988 }19891990 fn owned_amount(1991 subject: &T::CrossAccountId,1992 collection: &CollectionHandle<T>,1993 item_id: TokenId,1994 ) -> Result<Option<u128>, DispatchError> {1995 collection.consume_sload()?;1996 Ok(Self::owned_amount_unchecked(subject, collection, item_id))1997 }19981999 fn owned_amount_unchecked(2000 subject: &T::CrossAccountId,2001 target_collection: &CollectionHandle<T>,2002 item_id: TokenId,2003 ) -> Option<u128> {2004 let collection_id = target_collection.id;20052006 match target_collection.mode {2007 CollectionMode::NFT => {2008 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)2009 }2010 CollectionMode::Fungible(_) => {2011 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)2012 }2013 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2014 .owner2015 .iter()2016 .find(|i| i.owner == *subject)2017 .map(|i| i.fraction),2018 CollectionMode::Invalid => None,2019 }2020 }20212022 fn is_item_owner(2023 subject: &T::CrossAccountId,2024 target_collection: &CollectionHandle<T>,2025 item_id: TokenId,2026 ) -> Result<bool, DispatchError> {2027 Ok(match target_collection.mode {2028 CollectionMode::Fungible(_) => true,2029 _ => Self::owned_amount(subject, target_collection, item_id)?.is_some(),2030 })2031 }20322033 fn check_white_list(2034 collection: &CollectionHandle<T>,2035 address: &T::CrossAccountId,2036 ) -> DispatchResult {2037 collection.consume_sload()?;2038 ensure!(2039 <WhiteList<T>>::contains_key(collection.id, address.as_sub()),2040 Error::<T>::AddresNotInWhiteList,2041 );2042 Ok(())2043 }20442045 2046 2047 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2048 let collection_id = target_collection.id;2049 let exists = match target_collection.mode {2050 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2051 CollectionMode::Fungible(_) => true,2052 CollectionMode::ReFungible => {2053 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)2054 }2055 _ => false,2056 };20572058 ensure!(exists, Error::<T>::TokenNotFound);2059 Ok(())2060 }20612062 fn transfer_fungible(2063 collection: &CollectionHandle<T>,2064 value: u128,2065 owner: &T::CrossAccountId,2066 recipient: &T::CrossAccountId,2067 ) -> DispatchResult {2068 let collection_id = collection.id;20692070 collection.consume_sload()?;2071 collection.consume_sload()?;2072 let mut recipient_balance = <FungibleItemList<T>>::get(collection_id, recipient.as_sub());2073 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());20742075 recipient_balance.value = recipient_balance2076 .value2077 .checked_add(value)2078 .ok_or(Error::<T>::NumOverflow)?;2079 balance.value = balance2080 .value2081 .checked_sub(value)2082 .ok_or(Error::<T>::TokenValueTooLow)?;20832084 2085 collection.consume_sstore()?;2086 collection.consume_sstore()?;2087 if balance.value != 0 {2088 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value);2089 } else {2090 <Balance<T>>::remove(collection_id, owner.as_sub());2091 }2092 <Balance<T>>::insert(collection_id, recipient.as_sub(), recipient_balance.value);20932094 2095 collection.consume_sstore()?;2096 collection.consume_sstore()?;2097 if balance.value != 0 {2098 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2099 } else {2100 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2101 }2102 <FungibleItemList<T>>::insert(collection_id, recipient.as_sub(), recipient_balance);21032104 collection.log(ERC20Events::Transfer {2105 from: *owner.as_eth(),2106 to: *recipient.as_eth(),2107 value: value.into(),2108 })?;2109 Self::deposit_event(RawEvent::Transfer(2110 collection.id,2111 1,2112 owner.clone(),2113 recipient.clone(),2114 value,2115 ));21162117 Ok(())2118 }21192120 fn transfer_refungible(2121 collection: &CollectionHandle<T>,2122 item_id: TokenId,2123 value: u128,2124 owner: T::CrossAccountId,2125 new_owner: T::CrossAccountId,2126 ) -> DispatchResult {2127 let collection_id = collection.id;2128 collection.consume_sload()?;2129 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2130 .ok_or(Error::<T>::TokenNotFound)?;21312132 let item = full_item2133 .owner2134 .iter()2135 .find(|i| i.owner == owner)2136 .ok_or(Error::<T>::TokenNotFound)?;2137 let amount = item.fraction;21382139 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21402141 collection.consume_sload()?;2142 2143 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2144 .checked_sub(value)2145 .ok_or(Error::<T>::NumOverflow)?;2146 collection.consume_sstore()?;2147 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21482149 collection.consume_sload()?;2150 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2151 .checked_add(value)2152 .ok_or(Error::<T>::NumOverflow)?;2153 collection.consume_sstore()?;2154 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21552156 let old_owner = item.owner.clone();2157 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21582159 let mut new_full_item = full_item.clone();2160 2161 if amount == value && !new_owner_has_account {2162 2163 2164 new_full_item2165 .owner2166 .iter_mut()2167 .find(|i| i.owner == owner)2168 .expect("old owner does present in refungible")2169 .owner = new_owner.clone();2170 collection.consume_sstore()?;2171 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21722173 2174 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;2175 } else {2176 new_full_item2177 .owner2178 .iter_mut()2179 .find(|i| i.owner == owner)2180 .expect("old owner does present in refungible")2181 .fraction -= value;21822183 2184 if new_owner_has_account {2185 2186 new_full_item2187 .owner2188 .iter_mut()2189 .find(|i| i.owner == new_owner)2190 .expect("new owner has account")2191 .fraction += value;2192 } else {2193 2194 new_full_item.owner.push(Ownership {2195 owner: new_owner.clone(),2196 fraction: value,2197 });2198 Self::add_token_index(collection, item_id, &new_owner)?;2199 }22002201 collection.consume_sstore()?;2202 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2203 }22042205 Self::deposit_event(RawEvent::Transfer(2206 collection.id,2207 item_id,2208 owner,2209 new_owner,2210 amount,2211 ));22122213 Ok(())2214 }22152216 fn transfer_nft(2217 collection: &CollectionHandle<T>,2218 item_id: TokenId,2219 sender: T::CrossAccountId,2220 new_owner: T::CrossAccountId,2221 ) -> DispatchResult {2222 let collection_id = collection.id;2223 collection.consume_sload()?;2224 let mut item =2225 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;22262227 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);22282229 collection.consume_sload()?;2230 2231 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2232 .checked_sub(1)2233 .ok_or(Error::<T>::NumOverflow)?;2234 collection.consume_sstore()?;2235 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);22362237 collection.consume_sload()?;2238 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2239 .checked_add(1)2240 .ok_or(Error::<T>::NumOverflow)?;2241 collection.consume_sstore()?;2242 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);22432244 2245 let old_owner = item.owner.clone();2246 item.owner = new_owner.clone();2247 collection.consume_sstore()?;2248 <NftItemList<T>>::insert(collection_id, item_id, item);22492250 2251 Self::move_token_index(collection, item_id, &old_owner, &new_owner)?;22522253 collection.log(ERC721Events::Transfer {2254 from: *sender.as_eth(),2255 to: *new_owner.as_eth(),2256 token_id: item_id.into(),2257 })?;2258 Self::deposit_event(RawEvent::Transfer(2259 collection.id,2260 item_id,2261 sender,2262 new_owner,2263 1,2264 ));22652266 Ok(())2267 }22682269 fn set_re_fungible_variable_data(2270 collection: &CollectionHandle<T>,2271 item_id: TokenId,2272 data: Vec<u8>,2273 ) -> DispatchResult {2274 let collection_id = collection.id;2275 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2276 .ok_or(Error::<T>::TokenNotFound)?;22772278 item.variable_data = data;22792280 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22812282 Ok(())2283 }22842285 fn set_nft_variable_data(2286 collection: &CollectionHandle<T>,2287 item_id: TokenId,2288 data: Vec<u8>,2289 ) -> DispatchResult {2290 let collection_id = collection.id;2291 let mut item =2292 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;22932294 item.variable_data = data;22952296 <NftItemList<T>>::insert(collection_id, item_id, item);22972298 Ok(())2299 }23002301 #[allow(dead_code)]2302 fn init_collection(item: &Collection<T>) {2303 2304 assert!(2305 item.decimal_points <= MAX_DECIMAL_POINTS,2306 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2307 );2308 assert!(2309 item.name.len() <= 64,2310 "Collection name can not be longer than 63 char"2311 );2312 assert!(2313 item.name.len() <= 256,2314 "Collection description can not be longer than 255 char"2315 );2316 assert!(2317 item.token_prefix.len() <= 16,2318 "Token prefix can not be longer than 15 char"2319 );23202321 2322 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();23232324 CreatedCollectionCount::put(next_id);2325 }23262327 #[allow(dead_code)]2328 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2329 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23302331 Self::add_token_index(2332 &CollectionHandle::get(collection_id).unwrap(),2333 current_index,2334 &item.owner,2335 )2336 .unwrap();23372338 <ItemListIndex>::insert(collection_id, current_index);23392340 2341 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2342 .checked_add(1)2343 .unwrap();2344 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2345 }23462347 #[allow(dead_code)]2348 fn init_fungible_token(2349 collection_id: CollectionId,2350 owner: &T::CrossAccountId,2351 item: &FungibleItemType,2352 ) {2353 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23542355 Self::add_token_index(2356 &CollectionHandle::get(collection_id).unwrap(),2357 current_index,2358 owner,2359 )2360 .unwrap();23612362 <ItemListIndex>::insert(collection_id, current_index);23632364 2365 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2366 .checked_add(item.value)2367 .unwrap();2368 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2369 }23702371 #[allow(dead_code)]2372 fn init_refungible_token(2373 collection_id: CollectionId,2374 item: &ReFungibleItemType<T::CrossAccountId>,2375 ) {2376 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();23772378 let value = item.owner.first().unwrap().fraction;2379 let owner = item.owner.first().unwrap().owner.clone();23802381 Self::add_token_index(2382 &CollectionHandle::get(collection_id).unwrap(),2383 current_index,2384 &owner,2385 )2386 .unwrap();23872388 <ItemListIndex>::insert(collection_id, current_index);23892390 2391 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2392 .checked_add(value)2393 .unwrap();2394 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2395 }23962397 fn add_token_index(2398 collection: &CollectionHandle<T>,2399 item_index: TokenId,2400 owner: &T::CrossAccountId,2401 ) -> DispatchResult {2402 2403 collection.consume_sload()?;2404 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2405 2406 collection.consume_sload()?;2407 let count = <AccountItemCount<T>>::get(owner.as_sub());2408 ensure!(2409 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2410 Error::<T>::AddressOwnershipLimitExceeded2411 );24122413 collection.consume_sstore()?;2414 <AccountItemCount<T>>::insert(2415 owner.as_sub(),2416 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2417 );2418 } else {2419 collection.consume_sstore()?;2420 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2421 }24222423 collection.consume_sload()?;2424 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2425 if list_exists {2426 collection.consume_sload()?;2427 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2428 let item_contains = list.contains(&item_index.clone());24292430 if !item_contains {2431 list.push(item_index);2432 }24332434 collection.consume_sstore()?;2435 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2436 } else {2437 let itm = vec![item_index];2438 collection.consume_sstore()?;2439 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), itm);2440 }24412442 Ok(())2443 }24442445 fn remove_token_index(2446 collection: &CollectionHandle<T>,2447 item_index: TokenId,2448 owner: &T::CrossAccountId,2449 ) -> DispatchResult {2450 2451 collection.consume_sload()?;2452 collection.consume_sstore()?;2453 <AccountItemCount<T>>::insert(2454 owner.as_sub(),2455 <AccountItemCount<T>>::get(owner.as_sub())2456 .checked_sub(1)2457 .ok_or(Error::<T>::NumOverflow)?,2458 );24592460 collection.consume_sload()?;2461 let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());2462 if list_exists {2463 collection.consume_sload()?;2464 let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());2465 let item_contains = list.contains(&item_index.clone());24662467 if item_contains {2468 list.retain(|&item| item != item_index);2469 collection.consume_sstore()?;2470 <AddressTokens<T>>::insert(collection.id, owner.as_sub(), list);2471 }2472 }24732474 Ok(())2475 }24762477 fn move_token_index(2478 collection: &CollectionHandle<T>,2479 item_index: TokenId,2480 old_owner: &T::CrossAccountId,2481 new_owner: &T::CrossAccountId,2482 ) -> DispatchResult {2483 Self::remove_token_index(collection, item_index, old_owner)?;2484 Self::add_token_index(collection, item_index, new_owner)?;24852486 Ok(())2487 }2488}24892490sp_api::decl_runtime_apis! {2491 pub trait NftApi {2492 2493 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2494 }2495}