123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910extern crate alloc;1112pub use serde::{Serialize, Deserialize};1314pub use frame_support::{15 construct_runtime, decl_event, decl_module, decl_storage, decl_error,16 dispatch::DispatchResult,17 ensure, fail, parameter_types,18 traits::{19 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,20 Randomness, IsSubType, WithdrawReasons,21 },22 weights::{23 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},24 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,25 WeightToFeePolynomial, DispatchClass,26 },27 StorageValue,28 transactional,29};3031use frame_system::{self as system, ensure_signed, ensure_root};32use sp_core::H160;33use sp_runtime::sp_std::prelude::Vec;34use core::ops::{Deref, DerefMut};35use core::cell::RefCell;36use nft_data_structs::{37 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,38 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,39 CollectionId, CollectionMode, TokenId, 40 SchemaVersion, SponsorshipState, Ownership,41 NftItemType, FungibleItemType, ReFungibleItemType42};43use pallet_ethereum::EthereumTransactionSender;4445#[cfg(test)]46mod mock;4748#[cfg(test)]49mod tests;5051mod default_weights;52mod eth;5354pub use eth::NftErcSupport;55pub use eth::account::*;56use eth::erc::{ERC20Events, ERC721Events};5758#[cfg(feature = "runtime-benchmarks")]59mod benchmarking;6061pub trait WeightInfo {62 fn create_collection() -> Weight;63 fn destroy_collection() -> Weight;64 fn add_to_white_list() -> Weight;65 fn remove_from_white_list() -> Weight;66 fn set_public_access_mode() -> Weight;67 fn set_mint_permission() -> Weight;68 fn change_collection_owner() -> Weight;69 fn add_collection_admin() -> Weight;70 fn remove_collection_admin() -> Weight;71 fn set_collection_sponsor() -> Weight;72 fn confirm_sponsorship() -> Weight;73 fn remove_collection_sponsor() -> Weight;74 fn create_item(s: usize) -> Weight;75 fn burn_item() -> Weight;76 fn transfer() -> Weight;77 fn approve() -> Weight;78 fn transfer_from() -> Weight;79 fn set_offchain_schema() -> Weight;80 fn set_const_on_chain_schema() -> Weight;81 fn set_variable_on_chain_schema() -> Weight;82 fn set_variable_meta_data() -> Weight;83 fn enable_contract_sponsoring() -> Weight;84 fn set_schema_version() -> Weight;85 fn set_chain_limits() -> Weight;86 fn set_contract_sponsoring_rate_limit() -> Weight;87 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;88 fn toggle_contract_white_list() -> Weight;89 fn add_to_contract_white_list() -> Weight;90 fn remove_from_contract_white_list() -> Weight;91 fn set_collection_limits() -> Weight;92}9394decl_error! {95 96 pub enum Error for Module<T: Config> {97 98 TotalCollectionsLimitExceeded,99 100 CollectionDecimalPointLimitExceeded, 101 102 CollectionNameLimitExceeded, 103 104 CollectionDescriptionLimitExceeded, 105 106 CollectionTokenPrefixLimitExceeded,107 108 CollectionNotFound,109 110 TokenNotFound,111 112 AdminNotFound,113 114 NumOverflow, 115 116 AlreadyAdmin, 117 118 NoPermission,119 120 ConfirmUnsetSponsorFail,121 122 PublicMintingNotAllowed,123 124 MustBeTokenOwner,125 126 TokenValueTooLow,127 128 NftSizeLimitExceeded,129 130 ApproveNotFound,131 132 TokenValueNotEnough,133 134 ApproveRequired,135 136 AddresNotInWhiteList,137 138 CollectionAdminsLimitExceeded,139 140 AddressOwnershipLimitExceeded,141 142 EmptyArgument,143 144 TokenConstDataLimitExceeded,145 146 TokenVariableDataLimitExceeded,147 148 NotNftDataUsedToMintNftCollectionToken,149 150 NotFungibleDataUsedToMintFungibleCollectionToken,151 152 NotReFungibleDataUsedToMintReFungibleCollectionToken,153 154 UnexpectedCollectionType,155 156 CantStoreMetadataInFungibleTokens,157 158 CollectionTokenLimitExceeded,159 160 AccountTokenLimitExceeded,161 162 CollectionLimitBoundsExceeded,163 164 OwnerPermissionsCantBeReverted,165 166 SchemaDataLimitExceeded,167 168 WrongRefungiblePieces,169 170 BadCreateRefungibleCall,171 172 OutOfGas,173 }174}175176pub struct CollectionHandle<T: Config> {177 pub id: CollectionId,178 collection: Collection<T>,179 logs: eth::log::LogRecorder,180 evm_address: H160,181 gas_limit: RefCell<u64>,182}183impl<T: Config> CollectionHandle<T> {184 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {185 <CollectionById<T>>::get(id)186 .map(|collection| Self {187 id,188 collection,189 logs: eth::log::LogRecorder::default(),190 evm_address: eth::collection_id_to_address(id),191 gas_limit: RefCell::new(gas_limit),192 })193 }194 pub fn get(id: CollectionId) -> Option<Self> {195 Self::get_with_gas_limit(id, u64::MAX)196 }197 pub fn gas_left(&self) -> u64 {198 *self.gas_limit.borrow()199 }200 pub fn consume_gas(&self, gas: u64) -> DispatchResult {201 let mut gas_limit = self.gas_limit.borrow_mut();202 if *gas_limit < gas {203 fail!(Error::<T>::OutOfGas);204 }205 *gas_limit -= gas;206 Ok(())207 }208 pub fn log(&self, log: impl evm_coder::ToLog) {209 self.logs.log(log.to_log(self.evm_address))210 }211 pub fn into_inner(self) -> Collection<T> {212 self.collection.clone()213 }214}215impl<T: Config> Deref for CollectionHandle<T> {216 type Target = Collection<T>;217218 fn deref(&self) -> &Self::Target {219 &self.collection220 }221}222223impl<T: Config> DerefMut for CollectionHandle<T> {224 fn deref_mut(&mut self) -> &mut Self::Target {225 &mut self.collection226 }227}228229pub trait Config: system::Config + Sized {230 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;231232 233 type WeightInfo: WeightInfo;234235 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;236 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;237 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;238239 type CrossAccountId: CrossAccountId<Self::AccountId>;240 type Currency: Currency<Self::AccountId>;241 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;242 type TreasuryAccountId: Get<Self::AccountId>;243244 type EthereumChainId: Get<u64>;245 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;246}247248249250251252253254255256257258259260261262263264265266267268269270decl_storage! {271 trait Store for Module<T: Config> as Nft {272273 274 275 CreatedCollectionCount: u32;276 277 ChainVersion: u64;278 279 280 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;281 282283 284 pub ChainLimit get(fn chain_limit) config(): ChainLimits;285 286287 288 289 290 DestroyedCollectionCount: u32;291 292 293 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294 295296 297 298 299 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300 301 302 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303 304 305 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306 307308 309 310 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312 313 314 315 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317 318 319 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320 321 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322 323 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324 325326 327 328 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329 330331 332 333 334 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335 336 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337 338 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339 340 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341 342343 344 345 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346 347 348 349 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;350 351 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;352 353 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;354 355 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;356 357 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 358 359 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 360 361 }362 add_extra_genesis {363 build(|config: &GenesisConfig<T>| {364 365 for (_num, _c) in &config.collection_id {366 <Module<T>>::init_collection(_c);367 }368369 for (_num, _c, _i) in &config.nft_item_id {370 <Module<T>>::init_nft_token(*_c, _i);371 }372373 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {374 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);375 }376377 for (_num, _c, _i) in &config.refungible_item_id {378 <Module<T>>::init_refungible_token(*_c, _i);379 }380 })381 }382}383384decl_event!(385 pub enum Event<T>386 where387 AccountId = <T as frame_system::Config>::AccountId,388 CrossAccountId = <T as Config>::CrossAccountId,389 {390 391 392 393 394 395 396 397 398 399 CollectionCreated(CollectionId, u8, AccountId),400401 402 403 404 405 406 407 408 409 410 ItemCreated(CollectionId, TokenId, CrossAccountId),411412 413 414 415 416 417 418 419 ItemDestroyed(CollectionId, TokenId),420421 422 423 424 425 426 427 428 429 430 431 432 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),433434 435 436 437 438 439 440 441 442 443 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),444 }445);446447decl_module! {448 pub struct Module<T: Config> for enum Call 449 where 450 origin: T::Origin451 {452 fn deposit_event() = default;453 type Error = Error<T>;454455 fn on_initialize(_now: T::BlockNumber) -> Weight {456 0457 }458459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 #[weight = <T as Config>::WeightInfo::create_collection()]476 #[transactional]477 pub fn create_collection(origin,478 collection_name: Vec<u16>,479 collection_description: Vec<u16>,480 token_prefix: Vec<u8>,481 mode: CollectionMode) -> DispatchResult {482483 484 let who = ensure_signed(origin)?;485486 487 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();488 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(489 &T::TreasuryAccountId::get(),490 T::CollectionCreationPrice::get(),491 ));492 <T as Config>::Currency::settle(493 &who,494 imbalance,495 WithdrawReasons::TRANSFER,496 ExistenceRequirement::KeepAlive,497 ).map_err(|_| Error::<T>::NoPermission)?;498499 let decimal_points = match mode {500 CollectionMode::Fungible(points) => points,501 _ => 0502 };503504 let chain_limit = ChainLimit::get();505506 let created_count = CreatedCollectionCount::get();507 let destroyed_count = DestroyedCollectionCount::get();508509 510 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);511512 513 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);514 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);515 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);516 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);517518 519 let next_id = created_count520 .checked_add(1)521 .ok_or(Error::<T>::NumOverflow)?;522523 CreatedCollectionCount::put(next_id);524525 let limits = CollectionLimits {526 sponsored_data_size: chain_limit.custom_data_limit,527 ..Default::default()528 };529530 531 let new_collection = Collection {532 owner: who.clone(),533 name: collection_name,534 mode: mode.clone(),535 mint_mode: false,536 access: AccessMode::Normal,537 description: collection_description,538 decimal_points: decimal_points,539 token_prefix: token_prefix,540 offchain_schema: Vec::new(),541 schema_version: SchemaVersion::ImageURL,542 sponsorship: SponsorshipState::Disabled,543 variable_on_chain_schema: Vec::new(),544 const_on_chain_schema: Vec::new(),545 limits,546 };547548 549 <CollectionById<T>>::insert(next_id, new_collection);550551 552 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));553554 Ok(())555 }556557 558 559 560 561 562 563 564 565 566 #[weight = <T as Config>::WeightInfo::destroy_collection()]567 #[transactional]568 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {569570 let sender = ensure_signed(origin)?;571 let collection = Self::get_collection(collection_id)?;572 Self::check_owner_permissions(&collection, &sender)?;573 if !collection.limits.owner_can_destroy {574 fail!(Error::<T>::NoPermission);575 }576577 <AddressTokens<T>>::remove_prefix(collection_id);578 <Allowances<T>>::remove_prefix(collection_id);579 <Balance<T>>::remove_prefix(collection_id);580 <ItemListIndex>::remove(collection_id);581 <AdminList<T>>::remove(collection_id);582 <CollectionById<T>>::remove(collection_id);583 <WhiteList<T>>::remove_prefix(collection_id);584585 <NftItemList<T>>::remove_prefix(collection_id);586 <FungibleItemList<T>>::remove_prefix(collection_id);587 <ReFungibleItemList<T>>::remove_prefix(collection_id);588589 <NftTransferBasket<T>>::remove_prefix(collection_id);590 <FungibleTransferBasket<T>>::remove_prefix(collection_id);591 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);592593 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);594595 DestroyedCollectionCount::put(DestroyedCollectionCount::get()596 .checked_add(1)597 .ok_or(Error::<T>::NumOverflow)?);598599 Ok(())600 }601602 603 604 605 606 607 608 609 610 611 612 613 614 #[weight = <T as Config>::WeightInfo::add_to_white_list()]615 #[transactional]616 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{617618 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);619 let collection = Self::get_collection(collection_id)?;620621 Self::toggle_white_list_internal(622 &sender,623 &collection,624 &address,625 true,626 )?;627628 Ok(())629 }630631 632 633 634 635 636 637 638 639 640 641 642 643 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]644 #[transactional]645 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{646647 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);648 let collection = Self::get_collection(collection_id)?;649650 Self::toggle_white_list_internal(651 &sender,652 &collection,653 &address,654 false,655 )?;656657 Ok(())658 }659660 661 662 663 664 665 666 667 668 669 670 671 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]672 #[transactional]673 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult674 {675 let sender = ensure_signed(origin)?;676677 let mut target_collection = Self::get_collection(collection_id)?;678 Self::check_owner_permissions(&target_collection, &sender)?;679 target_collection.access = mode;680 Self::save_collection(target_collection);681682 Ok(())683 }684685 686 687 688 689 690 691 692 693 694 695 696 697 698 #[weight = <T as Config>::WeightInfo::set_mint_permission()]699 #[transactional]700 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult701 {702 let sender = ensure_signed(origin)?;703704 let mut target_collection = Self::get_collection(collection_id)?;705 Self::check_owner_permissions(&target_collection, &sender)?;706 target_collection.mint_mode = mint_permission;707 Self::save_collection(target_collection);708709 Ok(())710 }711712 713 714 715 716 717 718 719 720 721 722 723 #[weight = <T as Config>::WeightInfo::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 Self::save_collection(target_collection);732733 Ok(())734 }735736 737 738 739 740 741 742 743 744 745 746 747 748 749 #[weight = <T as Config>::WeightInfo::add_collection_admin()]750 #[transactional]751 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {752 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);753 let collection = Self::get_collection(collection_id)?;754 Self::check_owner_or_admin_permissions(&collection, &sender)?;755 let mut admin_arr = <AdminList<T>>::get(collection_id);756757 match admin_arr.binary_search(&new_admin_id) {758 Ok(_) => {},759 Err(idx) => {760 let limits = ChainLimit::get();761 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);762 admin_arr.insert(idx, new_admin_id);763 <AdminList<T>>::insert(collection_id, admin_arr);764 }765 }766 Ok(())767 }768769 770 771 772 773 774 775 776 777 778 779 780 781 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]782 #[transactional]783 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {784 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);785 let collection = Self::get_collection(collection_id)?;786 Self::check_owner_or_admin_permissions(&collection, &sender)?;787 let mut admin_arr = <AdminList<T>>::get(collection_id);788789 match admin_arr.binary_search(&account_id) {790 Ok(idx) => {791 admin_arr.remove(idx);792 <AdminList<T>>::insert(collection_id, admin_arr);793 },794 Err(_) => {}795 }796 Ok(())797 }798799 800 801 802 803 804 805 806 807 808 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]809 #[transactional]810 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {811 let sender = ensure_signed(origin)?;812 let mut target_collection = Self::get_collection(collection_id)?;813 Self::check_owner_permissions(&target_collection, &sender)?;814815 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);816 Self::save_collection(target_collection);817818 Ok(())819 }820821 822 823 824 825 826 827 828 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]829 #[transactional]830 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {831 let sender = ensure_signed(origin)?;832833 let mut target_collection = Self::get_collection(collection_id)?;834 ensure!(835 target_collection.sponsorship.pending_sponsor() == Some(&sender),836 Error::<T>::ConfirmUnsetSponsorFail837 );838839 target_collection.sponsorship = SponsorshipState::Confirmed(sender);840 Self::save_collection(target_collection);841842 Ok(())843 }844845 846 847 848 849 850 851 852 853 854 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]855 #[transactional]856 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {857 let sender = ensure_signed(origin)?;858859 let mut target_collection = Self::get_collection(collection_id)?;860 Self::check_owner_permissions(&target_collection, &sender)?;861862 target_collection.sponsorship = SponsorshipState::Disabled;863 Self::save_collection(target_collection);864865 Ok(())866 }867868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891892 #[weight = <T as Config>::WeightInfo::create_item(data.len())]893 #[transactional]894 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897898 Self::create_item_internal(&sender, &collection, &owner, data)?;899900 Self::submit_logs(collection)?;901 Ok(())902 }903904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()923 .map(|data| { data.len() })924 .sum())]925 #[transactional]926 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {927928 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);929 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);930 let collection = Self::get_collection(collection_id)?;931932 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;933934 Self::submit_logs(collection)?;935 Ok(())936 }937938 939 940 941 942 943 944 945 946 947 948 949 950 951 #[weight = <T as Config>::WeightInfo::burn_item()]952 #[transactional]953 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {954955 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);956 let target_collection = Self::get_collection(collection_id)?;957958 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;959960 Self::submit_logs(target_collection)?;961 Ok(())962 }963964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 #[weight = <T as Config>::WeightInfo::transfer()]988 #[transactional]989 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {990 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);991 let collection = Self::get_collection(collection_id)?;992993 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;994995 Self::submit_logs(collection)?;996 Ok(())997 }998999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 #[weight = <T as Config>::WeightInfo::approve()]1015 #[transactional]1016 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1017 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1018 let collection = Self::get_collection(collection_id)?;10191020 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10211022 Self::submit_logs(collection)?;1023 Ok(())1024 }1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 #[weight = <T as Config>::WeightInfo::transfer_from()]1046 #[transactional]1047 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1048 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1049 let collection = Self::get_collection(collection_id)?;10501051 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10521053 Self::submit_logs(collection)?;1054 Ok(())1055 }1056 1057 1058 1059 1060 10611062 10631064 10651066 1067 10681069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1082 #[transactional]1083 pub fn set_variable_meta_data (1084 origin,1085 collection_id: CollectionId,1086 item_id: TokenId,1087 data: Vec<u8>1088 ) -> DispatchResult {1089 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1090 1091 let collection = Self::get_collection(collection_id)?;10921093 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10941095 Ok(())1096 }1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 #[weight = <T as Config>::WeightInfo::set_schema_version()]1113 #[transactional]1114 pub fn set_schema_version(1115 origin,1116 collection_id: CollectionId,1117 version: SchemaVersion1118 ) -> DispatchResult {1119 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1120 let mut target_collection = Self::get_collection(collection_id)?;1121 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1122 target_collection.schema_version = version;1123 Self::save_collection(target_collection);11241125 Ok(())1126 }11271128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1141 #[transactional]1142 pub fn set_offchain_schema(1143 origin,1144 collection_id: CollectionId,1145 schema: Vec<u8>1146 ) -> DispatchResult {1147 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1148 let mut target_collection = Self::get_collection(collection_id)?;1149 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11501151 1152 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11531154 target_collection.offchain_schema = schema;1155 Self::save_collection(target_collection);11561157 Ok(())1158 }11591160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1173 #[transactional]1174 pub fn set_const_on_chain_schema (1175 origin,1176 collection_id: CollectionId,1177 schema: Vec<u8>1178 ) -> DispatchResult {1179 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1180 let mut target_collection = Self::get_collection(collection_id)?;1181 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11821183 1184 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11851186 target_collection.const_on_chain_schema = schema;1187 Self::save_collection(target_collection);11881189 Ok(())1190 }11911192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1205 #[transactional]1206 pub fn set_variable_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 <= ChainLimit::get().variable_on_chain_schema_limit, "");12171218 target_collection.variable_on_chain_schema = schema;1219 Self::save_collection(target_collection);12201221 Ok(())1222 }12231224 1225 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1226 #[transactional]1227 pub fn set_chain_limits(1228 origin,1229 limits: ChainLimits1230 ) -> DispatchResult {12311232 #[cfg(not(feature = "runtime-benchmarks"))]1233 ensure_root(origin)?;12341235 <ChainLimit>::put(limits);1236 Ok(())1237 }12381239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1251 #[transactional]1252 pub fn enable_contract_sponsoring(1253 origin,1254 contract_address: T::AccountId,1255 enable: bool1256 ) -> DispatchResult {12571258 let sender = ensure_signed(origin)?;12591260 #[cfg(feature = "runtime-benchmarks")]1261 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12621263 Self::ensure_contract_owned(sender, &contract_address)?;12641265 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1266 Ok(())1267 }12681269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1287 #[transactional]1288 pub fn set_contract_sponsoring_rate_limit(1289 origin,1290 contract_address: T::AccountId,1291 rate_limit: T::BlockNumber1292 ) -> DispatchResult {1293 let sender = ensure_signed(origin)?;12941295 #[cfg(feature = "runtime-benchmarks")]1296 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12971298 Self::ensure_contract_owned(sender, &contract_address)?;1299 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1300 Ok(())1301 }13021303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1315 #[transactional]1316 pub fn toggle_contract_white_list(1317 origin,1318 contract_address: T::AccountId,1319 enable: bool1320 ) -> DispatchResult {1321 let sender = ensure_signed(origin)?;13221323 #[cfg(feature = "runtime-benchmarks")]1324 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13251326 Self::ensure_contract_owned(sender, &contract_address)?;1327 if enable {1328 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1329 } else {1330 <ContractWhiteListEnabled<T>>::remove(contract_address);1331 }1332 Ok(())1333 }1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1347 #[transactional]1348 pub fn add_to_contract_white_list(1349 origin,1350 contract_address: T::AccountId,1351 account_address: T::AccountId1352 ) -> DispatchResult {1353 let sender = ensure_signed(origin)?;13541355 #[cfg(feature = "runtime-benchmarks")]1356 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1357 1358 Self::ensure_contract_owned(sender, &contract_address)?; 1359 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1360 Ok(())1361 }13621363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1375 #[transactional]1376 pub fn remove_from_contract_white_list(1377 origin,1378 contract_address: T::AccountId,1379 account_address: T::AccountId1380 ) -> DispatchResult {1381 let sender = ensure_signed(origin)?;13821383 #[cfg(feature = "runtime-benchmarks")]1384 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13851386 Self::ensure_contract_owned(sender, &contract_address)?;1387 <ContractWhiteList<T>>::remove(contract_address, account_address);1388 Ok(())1389 }13901391 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1392 #[transactional]1393 pub fn set_collection_limits(1394 origin,1395 collection_id: u32,1396 new_limits: CollectionLimits<T::BlockNumber>,1397 ) -> DispatchResult {1398 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1399 let mut target_collection = Self::get_collection(collection_id)?;1400 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1401 let old_limits = &target_collection.limits;1402 let chain_limits = ChainLimit::get();14031404 1405 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1406 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1407 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1408 Error::<T>::CollectionLimitBoundsExceeded);14091410 1411 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1412 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14131414 ensure!(1415 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1416 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1417 Error::<T>::OwnerPermissionsCantBeReverted,1418 );14191420 target_collection.limits = new_limits;1421 Self::save_collection(target_collection);14221423 Ok(())1424 } 1425 }1426}14271428impl<T: Config> Module<T> {1429 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1430 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1431 Self::validate_create_item_args(&collection, &data)?;1432 Self::create_item_no_validation(&collection, owner, data)?;14331434 Ok(())1435 }14361437 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1438 target_collection.consume_gas(2000000)?;1439 1440 Self::is_correct_transfer(target_collection, &recipient)?;14411442 1443 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1444 Self::is_owner_or_admin_permissions(target_collection, &sender),1445 Error::<T>::NoPermission);14461447 if target_collection.access == AccessMode::WhiteList {1448 Self::check_white_list(target_collection, &sender)?;1449 Self::check_white_list(target_collection, &recipient)?;1450 }14511452 match target_collection.mode1453 {1454 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1455 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1456 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1457 _ => ()1458 };14591460 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));14611462 Ok(())1463 }14641465 pub fn approve_internal(1466 sender: &T::CrossAccountId,1467 spender: &T::CrossAccountId,1468 collection: &CollectionHandle<T>,1469 item_id: TokenId,1470 amount: u1281471 ) -> DispatchResult {1472 collection.consume_gas(2000000)?;1473 Self::token_exists(&collection, item_id)?;14741475 1476 let bypasses_limits = collection.limits.owner_can_transfer &&1477 Self::is_owner_or_admin_permissions(1478 &collection,1479 &sender,1480 );14811482 let allowance_limit = if bypasses_limits {1483 None1484 } else if let Some(amount) = Self::owned_amount(1485 &sender,1486 &collection,1487 item_id,1488 ) {1489 Some(amount)1490 } else {1491 fail!(Error::<T>::NoPermission);1492 };14931494 if collection.access == AccessMode::WhiteList {1495 Self::check_white_list(&collection, &sender)?;1496 Self::check_white_list(&collection, &spender)?;1497 }14981499 let allowance: u128 = amount1500 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1501 .ok_or(Error::<T>::NumOverflow)?;1502 if let Some(limit) = allowance_limit {1503 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1504 }1505 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);15061507 if matches!(collection.mode, CollectionMode::NFT) {1508 1509 collection.log(ERC721Events::Approval {1510 owner: *sender.as_eth(),1511 approved: *spender.as_eth(),1512 token_id: item_id.into(),1513 });1514 }15151516 if matches!(collection.mode, CollectionMode::Fungible(_)) {1517 1518 collection.log(ERC20Events::Approval {1519 owner: *sender.as_eth(),1520 spender: *spender.as_eth(),1521 value: allowance.into()1522 });1523 }15241525 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1526 Ok(())1527 }15281529 pub fn transfer_from_internal(1530 sender: &T::CrossAccountId,1531 from: &T::CrossAccountId,1532 recipient: &T::CrossAccountId,1533 collection: &CollectionHandle<T>,1534 item_id: TokenId,1535 amount: u128,1536 ) -> DispatchResult {1537 collection.consume_gas(2000000)?;1538 1539 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));15401541 1542 Self::is_correct_transfer(&collection, &recipient)?;15431544 1545 ensure!(1546 approval >= amount || 1547 (1548 collection.limits.owner_can_transfer &&1549 Self::is_owner_or_admin_permissions(&collection, &sender)1550 ),1551 Error::<T>::NoPermission1552 );15531554 if collection.access == AccessMode::WhiteList {1555 Self::check_white_list(&collection, &sender)?;1556 Self::check_white_list(&collection, &recipient)?;1557 }15581559 1560 let allowance = approval.saturating_sub(amount);1561 if allowance > 0 {1562 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1563 } else {1564 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1565 }15661567 match collection.mode {1568 CollectionMode::NFT => {1569 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1570 }1571 CollectionMode::Fungible(_) => {1572 Self::transfer_fungible(&collection, amount, &from, &recipient)?1573 }1574 CollectionMode::ReFungible => {1575 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1576 }1577 _ => ()1578 };15791580 if matches!(collection.mode, CollectionMode::Fungible(_)) {1581 collection.log(ERC20Events::Approval {1582 owner: *from.as_eth(),1583 spender: *sender.as_eth(),1584 value: allowance.into()1585 });1586 }15871588 Ok(())1589 }15901591 pub fn set_variable_meta_data_internal(1592 sender: &T::CrossAccountId,1593 collection: &CollectionHandle<T>, 1594 item_id: TokenId,1595 data: Vec<u8>,1596 ) -> DispatchResult {1597 Self::token_exists(&collection, item_id)?;15981599 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16001601 1602 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1603 Self::is_owner_or_admin_permissions(&collection, &sender),1604 Error::<T>::NoPermission);16051606 match collection.mode1607 {1608 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1609 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1610 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1611 _ => fail!(Error::<T>::UnexpectedCollectionType)1612 };16131614 Ok(())1615 }16161617 pub fn create_multiple_items_internal(1618 sender: &T::CrossAccountId,1619 collection: &CollectionHandle<T>,1620 owner: &T::CrossAccountId,1621 items_data: Vec<CreateItemData>,1622 ) -> DispatchResult {1623 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;16241625 for data in &items_data {1626 Self::validate_create_item_args(&collection, data)?;1627 }1628 for data in &items_data {1629 Self::create_item_no_validation(&collection, owner, data.clone())?;1630 }16311632 Ok(())1633 }16341635 pub fn burn_item_internal(1636 sender: &T::CrossAccountId,1637 collection: &CollectionHandle<T>,1638 item_id: TokenId,1639 value: u128,1640 ) -> DispatchResult {1641 ensure!(1642 Self::is_item_owner(&sender, &collection, item_id) ||1643 (1644 collection.limits.owner_can_transfer &&1645 Self::is_owner_or_admin_permissions(&collection, &sender)1646 ),1647 Error::<T>::NoPermission1648 );16491650 if collection.access == AccessMode::WhiteList {1651 Self::check_white_list(&collection, &sender)?;1652 }16531654 match collection.mode1655 {1656 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1657 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1658 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1659 _ => ()1660 };16611662 Ok(())1663 }16641665 pub fn toggle_white_list_internal(1666 sender: &T::CrossAccountId,1667 collection: &CollectionHandle<T>,1668 address: &T::CrossAccountId,1669 whitelisted: bool,1670 ) -> DispatchResult {1671 Self::check_owner_or_admin_permissions(&collection, &sender)?;16721673 if whitelisted {1674 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1675 } else {1676 <WhiteList<T>>::remove(collection.id, address.as_sub());1677 }16781679 Ok(())1680 }16811682 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1683 let collection_id = collection.id;16841685 1686 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1687 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1688 1689 Ok(())1690 }16911692 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1693 let collection_id = collection.id;16941695 1696 let total_items: u32 = ItemListIndex::get(collection_id)1697 .checked_add(amount)1698 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1699 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1700 .checked_add(amount)1701 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1702 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1703 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);17041705 if !Self::is_owner_or_admin_permissions(collection, &sender) {1706 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1707 Self::check_white_list(collection, owner)?;1708 Self::check_white_list(collection, sender)?;1709 }17101711 Ok(())1712 }17131714 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1715 match target_collection.mode1716 {1717 CollectionMode::NFT => {1718 if let CreateItemData::NFT(data) = data {1719 1720 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1721 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1722 } else {1723 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1724 }1725 },1726 CollectionMode::Fungible(_) => {1727 if let CreateItemData::Fungible(_) = data {1728 } else {1729 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1730 }1731 },1732 CollectionMode::ReFungible => {1733 if let CreateItemData::ReFungible(data) = data {17341735 1736 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1737 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17381739 1740 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1741 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1742 } else {1743 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1744 }1745 },1746 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1747 };17481749 Ok(())1750 }17511752 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1753 match data1754 {1755 CreateItemData::NFT(data) => {1756 let item = NftItemType {1757 owner: owner.clone(),1758 const_data: data.const_data,1759 variable_data: data.variable_data1760 };17611762 Self::add_nft_item(collection, item)?;1763 },1764 CreateItemData::Fungible(data) => {1765 Self::add_fungible_item(collection, &owner, data.value)?;1766 },1767 CreateItemData::ReFungible(data) => {1768 let mut owner_list = Vec::new();1769 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17701771 let item = ReFungibleItemType {1772 owner: owner_list,1773 const_data: data.const_data,1774 variable_data: data.variable_data1775 };17761777 Self::add_refungible_item(collection, item)?;1778 }1779 };17801781 Ok(())1782 }17831784 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1785 let collection_id = collection.id;17861787 1788 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17891790 1791 let item = FungibleItemType {1792 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1793 };1794 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17951796 1797 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1798 .checked_add(value)1799 .ok_or(Error::<T>::NumOverflow)?;1800 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18011802 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1803 Ok(())1804 }18051806 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1807 let collection_id = collection.id;18081809 let current_index = <ItemListIndex>::get(collection_id)1810 .checked_add(1)1811 .ok_or(Error::<T>::NumOverflow)?;1812 let itemcopy = item.clone();18131814 ensure!(1815 item.owner.len() == 1,1816 Error::<T>::BadCreateRefungibleCall,1817 );1818 let item_owner = item.owner.first().expect("only one owner is defined");18191820 let value = item_owner.fraction;1821 let owner = item_owner.owner.clone();18221823 Self::add_token_index(collection_id, current_index, &owner)?;18241825 <ItemListIndex>::insert(collection_id, current_index);1826 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18271828 1829 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830 .checked_add(value)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1835 Ok(())1836 }18371838 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1839 let collection_id = collection.id;18401841 let current_index = <ItemListIndex>::get(collection_id)1842 .checked_add(1)1843 .ok_or(Error::<T>::NumOverflow)?;18441845 let item_owner = item.owner.clone();1846 Self::add_token_index(collection_id, current_index, &item.owner)?;18471848 <ItemListIndex>::insert(collection_id, current_index);1849 <NftItemList<T>>::insert(collection_id, current_index, item);18501851 1852 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1853 .checked_add(1)1854 .ok_or(Error::<T>::NumOverflow)?;1855 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18561857 collection.log(ERC721Events::Transfer {1858 from: H160::default(),1859 to: *item_owner.as_eth(),1860 token_id: current_index.into(),1861 });1862 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1863 Ok(())1864 }18651866 fn burn_refungible_item(1867 collection: &CollectionHandle<T>,1868 item_id: TokenId,1869 owner: &T::CrossAccountId,1870 ) -> DispatchResult {1871 let collection_id = collection.id;18721873 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1874 .ok_or(Error::<T>::TokenNotFound)?;1875 let rft_balance = token1876 .owner1877 .iter()1878 .find(|&i| i.owner == *owner)1879 .ok_or(Error::<T>::TokenNotFound)?;1880 Self::remove_token_index(collection_id, item_id, owner)?;18811882 1883 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1884 .checked_sub(rft_balance.fraction)1885 .ok_or(Error::<T>::NumOverflow)?;1886 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18871888 1889 let index = token1890 .owner1891 .iter()1892 .position(|i| i.owner == *owner)1893 .expect("owned item is exists");1894 token.owner.remove(index);1895 let owner_count = token.owner.len();18961897 1898 if owner_count == 0 {1899 <ReFungibleItemList<T>>::remove(collection_id, item_id);1900 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1901 }1902 else {1903 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1904 }19051906 Ok(())1907 }19081909 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1910 let collection_id = collection.id;19111912 let item = <NftItemList<T>>::get(collection_id, item_id)1913 .ok_or(Error::<T>::TokenNotFound)?;1914 Self::remove_token_index(collection_id, item_id, &item.owner)?;19151916 1917 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1918 .checked_sub(1)1919 .ok_or(Error::<T>::NumOverflow)?;1920 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1921 <NftItemList<T>>::remove(collection_id, item_id);1922 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);19231924 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1925 Ok(())1926 }19271928 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1929 let collection_id = collection.id;19301931 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1932 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19331934 1935 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1936 .checked_sub(value)1937 .ok_or(Error::<T>::NumOverflow)?;1938 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19391940 if balance.value - value > 0 {1941 balance.value -= value;1942 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1943 }1944 else {1945 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1946 }19471948 collection.log(ERC20Events::Transfer {1949 from: *owner.as_eth(),1950 to: H160::default(),1951 value: value.into(),1952 });1953 Ok(())1954 }19551956 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1957 Ok(<CollectionHandle<T>>::get(collection_id)1958 .ok_or(Error::<T>::CollectionNotFound)?)1959 }19601961 fn save_collection(collection: CollectionHandle<T>) {1962 <CollectionById<T>>::insert(collection.id, collection.into_inner());1963 }19641965 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1966 if collection.logs.is_empty() {1967 return Ok(())1968 }1969 T::EthereumTransactionSender::submit_logs_transaction(1970 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1971 collection.logs.retrieve_logs(),1972 )1973 }19741975 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1976 ensure!(1977 *subject == target_collection.owner,1978 Error::<T>::NoPermission1979 );19801981 Ok(())1982 }19831984 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1985 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1986 }19871988 fn check_owner_or_admin_permissions(1989 collection: &CollectionHandle<T>,1990 subject: &T::CrossAccountId,1991 ) -> DispatchResult {1992 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);19931994 Ok(())1995 }19961997 fn owned_amount(1998 subject: &T::CrossAccountId,1999 target_collection: &CollectionHandle<T>,2000 item_id: TokenId,2001 ) -> Option<u128> {2002 let collection_id = target_collection.id;20032004 match target_collection.mode {2005 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2006 .then(|| 1),2007 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2008 .value),2009 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2010 .owner2011 .iter()2012 .find(|i| i.owner == *subject)2013 .map(|i| i.fraction),2014 CollectionMode::Invalid => None,2015 }2016 }20172018 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2019 match target_collection.mode {2020 CollectionMode::Fungible(_) => true,2021 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2022 }2023 }20242025 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2026 let collection_id = collection.id;20272028 let mes = Error::<T>::AddresNotInWhiteList;2029 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);20302031 Ok(())2032 }20332034 2035 2036 fn token_exists(2037 target_collection: &CollectionHandle<T>,2038 item_id: TokenId,2039 ) -> DispatchResult {2040 let collection_id = target_collection.id;2041 let exists = match target_collection.mode2042 {2043 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2044 CollectionMode::Fungible(_) => true,2045 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2046 _ => false2047 };20482049 ensure!(exists == true, Error::<T>::TokenNotFound);2050 Ok(())2051 }20522053 fn transfer_fungible(2054 collection: &CollectionHandle<T>,2055 value: u128,2056 owner: &T::CrossAccountId,2057 recipient: &T::CrossAccountId,2058 ) -> DispatchResult {2059 let collection_id = collection.id;20602061 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2062 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20632064 2065 Self::add_fungible_item(collection, recipient, value)?;20662067 2068 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);20692070 2071 if balance.value == value {2072 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2073 }2074 else {2075 balance.value -= value;2076 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2077 }20782079 collection.log(ERC20Events::Transfer {2080 from: *owner.as_eth(),2081 to: *recipient.as_eth(),2082 value: value.into(),2083 });2084 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));20852086 Ok(())2087 }20882089 fn transfer_refungible(2090 collection: &CollectionHandle<T>,2091 item_id: TokenId,2092 value: u128,2093 owner: T::CrossAccountId,2094 new_owner: T::CrossAccountId,2095 ) -> DispatchResult {2096 let collection_id = collection.id;2097 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2098 .ok_or(Error::<T>::TokenNotFound)?;20992100 let item = full_item2101 .owner2102 .iter()2103 .filter(|i| i.owner == owner)2104 .next()2105 .ok_or(Error::<T>::TokenNotFound)?;2106 let amount = item.fraction;21072108 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21092110 2111 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2112 .checked_sub(value)2113 .ok_or(Error::<T>::NumOverflow)?;2114 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21152116 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2117 .checked_add(value)2118 .ok_or(Error::<T>::NumOverflow)?;2119 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21202121 let old_owner = item.owner.clone();2122 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21232124 2125 if amount == value && !new_owner_has_account {2126 2127 2128 let mut new_full_item = full_item.clone();2129 new_full_item2130 .owner2131 .iter_mut()2132 .find(|i| i.owner == owner)2133 .expect("old owner does present in refungible")2134 .owner = new_owner.clone();2135 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21362137 2138 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2139 } else {2140 let mut new_full_item = full_item.clone();2141 new_full_item2142 .owner2143 .iter_mut()2144 .find(|i| i.owner == owner)2145 .expect("old owner does present in refungible")2146 .fraction -= value;21472148 2149 if new_owner_has_account {2150 2151 new_full_item2152 .owner2153 .iter_mut()2154 .find(|i| i.owner == new_owner)2155 .expect("new owner has account")2156 .fraction += value;2157 } else {2158 2159 new_full_item.owner.push(Ownership {2160 owner: new_owner.clone(),2161 fraction: value,2162 });2163 Self::add_token_index(collection_id, item_id, &new_owner)?;2164 }21652166 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2167 }21682169 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));21702171 Ok(())2172 }21732174 fn transfer_nft(2175 collection: &CollectionHandle<T>,2176 item_id: TokenId,2177 sender: T::CrossAccountId,2178 new_owner: T::CrossAccountId,2179 ) -> DispatchResult {2180 let collection_id = collection.id;2181 let mut item = <NftItemList<T>>::get(collection_id, item_id)2182 .ok_or(Error::<T>::TokenNotFound)?;21832184 ensure!(2185 sender == item.owner,2186 Error::<T>::MustBeTokenOwner2187 );21882189 2190 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2191 .checked_sub(1)2192 .ok_or(Error::<T>::NumOverflow)?;2193 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21942195 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2196 .checked_add(1)2197 .ok_or(Error::<T>::NumOverflow)?;2198 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21992200 2201 let old_owner = item.owner.clone();2202 item.owner = new_owner.clone();2203 <NftItemList<T>>::insert(collection_id, item_id, item);22042205 2206 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22072208 collection.log(ERC721Events::Transfer {2209 from: *sender.as_eth(),2210 to: *new_owner.as_eth(),2211 token_id: item_id.into(),2212 });2213 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));22142215 Ok(())2216 }2217 2218 fn set_re_fungible_variable_data(2219 collection: &CollectionHandle<T>,2220 item_id: TokenId,2221 data: Vec<u8>2222 ) -> DispatchResult {2223 let collection_id = collection.id;2224 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2225 .ok_or(Error::<T>::TokenNotFound)?;22262227 item.variable_data = data;22282229 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22302231 Ok(())2232 }22332234 fn set_nft_variable_data(2235 collection: &CollectionHandle<T>,2236 item_id: TokenId,2237 data: Vec<u8>2238 ) -> DispatchResult {2239 let collection_id = collection.id;2240 let mut item = <NftItemList<T>>::get(collection_id, item_id)2241 .ok_or(Error::<T>::TokenNotFound)?;2242 2243 item.variable_data = data;22442245 <NftItemList<T>>::insert(collection_id, item_id, item);2246 2247 Ok(())2248 }22492250 #[allow(dead_code)]2251 fn init_collection(item: &Collection<T>) {2252 2253 assert!(2254 item.decimal_points <= MAX_DECIMAL_POINTS,2255 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2256 );2257 assert!(2258 item.name.len() <= 64,2259 "Collection name can not be longer than 63 char"2260 );2261 assert!(2262 item.name.len() <= 256,2263 "Collection description can not be longer than 255 char"2264 );2265 assert!(2266 item.token_prefix.len() <= 16,2267 "Token prefix can not be longer than 15 char"2268 );22692270 2271 let next_id = CreatedCollectionCount::get()2272 .checked_add(1)2273 .unwrap();22742275 CreatedCollectionCount::put(next_id);2276 }22772278 #[allow(dead_code)]2279 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2280 let current_index = <ItemListIndex>::get(collection_id)2281 .checked_add(1)2282 .unwrap();22832284 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22852286 <ItemListIndex>::insert(collection_id, current_index);22872288 2289 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2290 .checked_add(1)2291 .unwrap();2292 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2293 }22942295 #[allow(dead_code)]2296 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2297 let current_index = <ItemListIndex>::get(collection_id)2298 .checked_add(1)2299 .unwrap();23002301 Self::add_token_index(collection_id, current_index, owner).unwrap();23022303 <ItemListIndex>::insert(collection_id, current_index);23042305 2306 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2307 .checked_add(item.value)2308 .unwrap();2309 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2310 }23112312 #[allow(dead_code)]2313 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2314 let current_index = <ItemListIndex>::get(collection_id)2315 .checked_add(1)2316 .unwrap();23172318 let value = item.owner.first().unwrap().fraction;2319 let owner = item.owner.first().unwrap().owner.clone();23202321 Self::add_token_index(collection_id, current_index, &owner).unwrap();23222323 <ItemListIndex>::insert(collection_id, current_index);23242325 2326 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2327 .checked_add(value)2328 .unwrap();2329 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2330 }23312332 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2333 2334 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {23352336 2337 let count = <AccountItemCount<T>>::get(owner.as_sub());2338 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23392340 <AccountItemCount<T>>::insert(owner.as_sub(), count2341 .checked_add(1)2342 .ok_or(Error::<T>::NumOverflow)?);2343 }2344 else {2345 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2346 }23472348 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2349 if list_exists {2350 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2351 let item_contains = list.contains(&item_index.clone());23522353 if !item_contains {2354 list.push(item_index.clone());2355 }23562357 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2358 } else {2359 let mut itm = Vec::new();2360 itm.push(item_index.clone());2361 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2362 }23632364 Ok(())2365 }23662367 fn remove_token_index(2368 collection_id: CollectionId,2369 item_index: TokenId,2370 owner: &T::CrossAccountId,2371 ) -> DispatchResult {23722373 2374 <AccountItemCount<T>>::insert(owner.as_sub(), 2375 <AccountItemCount<T>>::get(owner.as_sub())2376 .checked_sub(1)2377 .ok_or(Error::<T>::NumOverflow)?);237823792380 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2381 if list_exists {2382 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2383 let item_contains = list.contains(&item_index.clone());23842385 if item_contains {2386 list.retain(|&item| item != item_index);2387 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2388 }2389 }23902391 Ok(())2392 }23932394 fn move_token_index(2395 collection_id: CollectionId,2396 item_index: TokenId,2397 old_owner: &T::CrossAccountId,2398 new_owner: &T::CrossAccountId,2399 ) -> DispatchResult {2400 Self::remove_token_index(collection_id, item_index, old_owner)?;2401 Self::add_token_index(collection_id, item_index, new_owner)?;24022403 Ok(())2404 }2405 2406 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2407 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);24082409 Ok(())2410 }2411}24122413sp_api::decl_runtime_apis! {2414 pub trait NftApi {2415 2416 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2417 }2418}