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;53mod sponsorship;54pub use sponsorship::NftSponsorshipHandler;5556pub use eth::NftErcSupport;57pub use eth::account::*;58use eth::erc::{ERC20Events, ERC721Events};5960#[cfg(feature = "runtime-benchmarks")]61mod benchmarking;6263pub trait WeightInfo {64 fn create_collection() -> Weight;65 fn destroy_collection() -> Weight;66 fn add_to_white_list() -> Weight;67 fn remove_from_white_list() -> Weight;68 fn set_public_access_mode() -> Weight;69 fn set_mint_permission() -> Weight;70 fn change_collection_owner() -> Weight;71 fn add_collection_admin() -> Weight;72 fn remove_collection_admin() -> Weight;73 fn set_collection_sponsor() -> Weight;74 fn confirm_sponsorship() -> Weight;75 fn remove_collection_sponsor() -> Weight;76 fn create_item(s: usize) -> Weight;77 fn burn_item() -> Weight;78 fn transfer() -> Weight;79 fn approve() -> Weight;80 fn transfer_from() -> Weight;81 fn set_offchain_schema() -> Weight;82 fn set_const_on_chain_schema() -> Weight;83 fn set_variable_on_chain_schema() -> Weight;84 fn set_variable_meta_data() -> Weight;85 fn enable_contract_sponsoring() -> Weight;86 fn set_schema_version() -> Weight;87 fn set_chain_limits() -> Weight;88 fn set_contract_sponsoring_rate_limit() -> Weight;89 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;90 fn toggle_contract_white_list() -> Weight;91 fn add_to_contract_white_list() -> Weight;92 fn remove_from_contract_white_list() -> Weight;93 fn set_collection_limits() -> Weight;94}9596decl_error! {97 98 pub enum Error for Module<T: Config> {99 100 TotalCollectionsLimitExceeded,101 102 CollectionDecimalPointLimitExceeded, 103 104 CollectionNameLimitExceeded, 105 106 CollectionDescriptionLimitExceeded, 107 108 CollectionTokenPrefixLimitExceeded,109 110 CollectionNotFound,111 112 TokenNotFound,113 114 AdminNotFound,115 116 NumOverflow, 117 118 AlreadyAdmin, 119 120 NoPermission,121 122 ConfirmUnsetSponsorFail,123 124 PublicMintingNotAllowed,125 126 MustBeTokenOwner,127 128 TokenValueTooLow,129 130 NftSizeLimitExceeded,131 132 ApproveNotFound,133 134 TokenValueNotEnough,135 136 ApproveRequired,137 138 AddresNotInWhiteList,139 140 CollectionAdminsLimitExceeded,141 142 AddressOwnershipLimitExceeded,143 144 EmptyArgument,145 146 TokenConstDataLimitExceeded,147 148 TokenVariableDataLimitExceeded,149 150 NotNftDataUsedToMintNftCollectionToken,151 152 NotFungibleDataUsedToMintFungibleCollectionToken,153 154 NotReFungibleDataUsedToMintReFungibleCollectionToken,155 156 UnexpectedCollectionType,157 158 CantStoreMetadataInFungibleTokens,159 160 CollectionTokenLimitExceeded,161 162 AccountTokenLimitExceeded,163 164 CollectionLimitBoundsExceeded,165 166 OwnerPermissionsCantBeReverted,167 168 SchemaDataLimitExceeded,169 170 WrongRefungiblePieces,171 172 BadCreateRefungibleCall,173 174 OutOfGas,175 }176}177178pub struct CollectionHandle<T: Config> {179 pub id: CollectionId,180 collection: Collection<T>,181 logs: eth::log::LogRecorder,182 evm_address: H160,183 gas_limit: RefCell<u64>,184}185impl<T: Config> CollectionHandle<T> {186 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {187 <CollectionById<T>>::get(id)188 .map(|collection| Self {189 id,190 collection,191 logs: eth::log::LogRecorder::default(),192 evm_address: eth::collection_id_to_address(id),193 gas_limit: RefCell::new(gas_limit),194 })195 }196 pub fn get(id: CollectionId) -> Option<Self> {197 Self::get_with_gas_limit(id, u64::MAX)198 }199 pub fn gas_left(&self) -> u64 {200 *self.gas_limit.borrow()201 }202 pub fn consume_gas(&self, gas: u64) -> DispatchResult {203 let mut gas_limit = self.gas_limit.borrow_mut();204 if *gas_limit < gas {205 fail!(Error::<T>::OutOfGas);206 }207 *gas_limit -= gas;208 Ok(())209 }210 pub fn log(&self, log: impl evm_coder::ToLog) {211 self.logs.log(log.to_log(self.evm_address))212 }213 pub fn into_inner(self) -> Collection<T> {214 self.collection.clone()215 }216}217impl<T: Config> Deref for CollectionHandle<T> {218 type Target = Collection<T>;219220 fn deref(&self) -> &Self::Target {221 &self.collection222 }223}224225impl<T: Config> DerefMut for CollectionHandle<T> {226 fn deref_mut(&mut self) -> &mut Self::Target {227 &mut self.collection228 }229}230231pub trait Config: system::Config + Sized {232 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;233234 235 type WeightInfo: WeightInfo;236237 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;238 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;239240 type CrossAccountId: CrossAccountId<Self::AccountId>;241 type Currency: Currency<Self::AccountId>;242 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;243 type TreasuryAccountId: Get<Self::AccountId>;244245 type EthereumChainId: Get<u64>;246 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;247}248249250251252253254255256257258259260261262263264265266267268269270271decl_storage! {272 trait Store for Module<T: Config> as Nft {273274 275 276 CreatedCollectionCount: u32;277 278 ChainVersion: u64;279 280 281 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;282 283284 285 pub ChainLimit get(fn chain_limit) config(): ChainLimits;286 287288 289 290 291 DestroyedCollectionCount: u32;292 293 294 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;295 296297 298 299 300 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;301 302 303 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;304 305 306 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;307 308309 310 311 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;312313 314 315 316 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;317318 319 320 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;321 322 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;323 324 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;325 326327 328 329 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;330 331332 333 334 335 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;336 337 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;338 339 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;340 341 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;342 343344 345 346 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;347 }348 add_extra_genesis {349 build(|config: &GenesisConfig<T>| {350 351 for (_num, _c) in &config.collection_id {352 <Module<T>>::init_collection(_c);353 }354355 for (_num, _c, _i) in &config.nft_item_id {356 <Module<T>>::init_nft_token(*_c, _i);357 }358359 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {360 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);361 }362363 for (_num, _c, _i) in &config.refungible_item_id {364 <Module<T>>::init_refungible_token(*_c, _i);365 }366 })367 }368}369370decl_event!(371 pub enum Event<T>372 where373 AccountId = <T as frame_system::Config>::AccountId,374 CrossAccountId = <T as Config>::CrossAccountId,375 {376 377 378 379 380 381 382 383 384 385 CollectionCreated(CollectionId, u8, AccountId),386387 388 389 390 391 392 393 394 395 396 ItemCreated(CollectionId, TokenId, CrossAccountId),397398 399 400 401 402 403 404 405 ItemDestroyed(CollectionId, TokenId),406407 408 409 410 411 412 413 414 415 416 417 418 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),419420 421 422 423 424 425 426 427 428 429 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),430 }431);432433decl_module! {434 pub struct Module<T: Config> for enum Call 435 where 436 origin: T::Origin437 {438 fn deposit_event() = default;439 type Error = Error<T>;440441 fn on_initialize(_now: T::BlockNumber) -> Weight {442 0443 }444445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 #[weight = <T as Config>::WeightInfo::create_collection()]462 #[transactional]463 pub fn create_collection(origin,464 collection_name: Vec<u16>,465 collection_description: Vec<u16>,466 token_prefix: Vec<u8>,467 mode: CollectionMode) -> DispatchResult {468469 470 let who = ensure_signed(origin)?;471472 473 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();474 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(475 &T::TreasuryAccountId::get(),476 T::CollectionCreationPrice::get(),477 ));478 <T as Config>::Currency::settle(479 &who,480 imbalance,481 WithdrawReasons::TRANSFER,482 ExistenceRequirement::KeepAlive,483 ).map_err(|_| Error::<T>::NoPermission)?;484485 let decimal_points = match mode {486 CollectionMode::Fungible(points) => points,487 _ => 0488 };489490 let chain_limit = ChainLimit::get();491492 let created_count = CreatedCollectionCount::get();493 let destroyed_count = DestroyedCollectionCount::get();494495 496 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);497498 499 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);500 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);501 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);502 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);503504 505 let next_id = created_count506 .checked_add(1)507 .ok_or(Error::<T>::NumOverflow)?;508509 CreatedCollectionCount::put(next_id);510511 let limits = CollectionLimits {512 sponsored_data_size: chain_limit.custom_data_limit,513 ..Default::default()514 };515516 517 let new_collection = Collection {518 owner: who.clone(),519 name: collection_name,520 mode: mode.clone(),521 mint_mode: false,522 access: AccessMode::Normal,523 description: collection_description,524 decimal_points: decimal_points,525 token_prefix: token_prefix,526 offchain_schema: Vec::new(),527 schema_version: SchemaVersion::ImageURL,528 sponsorship: SponsorshipState::Disabled,529 variable_on_chain_schema: Vec::new(),530 const_on_chain_schema: Vec::new(),531 limits,532 };533534 535 <CollectionById<T>>::insert(next_id, new_collection);536537 538 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));539540 Ok(())541 }542543 544 545 546 547 548 549 550 551 552 #[weight = <T as Config>::WeightInfo::destroy_collection()]553 #[transactional]554 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {555556 let sender = ensure_signed(origin)?;557 let collection = Self::get_collection(collection_id)?;558 Self::check_owner_permissions(&collection, &sender)?;559 if !collection.limits.owner_can_destroy {560 fail!(Error::<T>::NoPermission);561 }562563 <AddressTokens<T>>::remove_prefix(collection_id);564 <Allowances<T>>::remove_prefix(collection_id);565 <Balance<T>>::remove_prefix(collection_id);566 <ItemListIndex>::remove(collection_id);567 <AdminList<T>>::remove(collection_id);568 <CollectionById<T>>::remove(collection_id);569 <WhiteList<T>>::remove_prefix(collection_id);570571 <NftItemList<T>>::remove_prefix(collection_id);572 <FungibleItemList<T>>::remove_prefix(collection_id);573 <ReFungibleItemList<T>>::remove_prefix(collection_id);574575 <NftTransferBasket<T>>::remove_prefix(collection_id);576 <FungibleTransferBasket<T>>::remove_prefix(collection_id);577 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);578579 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);580581 DestroyedCollectionCount::put(DestroyedCollectionCount::get()582 .checked_add(1)583 .ok_or(Error::<T>::NumOverflow)?);584585 Ok(())586 }587588 589 590 591 592 593 594 595 596 597 598 599 600 #[weight = <T as Config>::WeightInfo::add_to_white_list()]601 #[transactional]602 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{603604 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);605 let collection = Self::get_collection(collection_id)?;606607 Self::toggle_white_list_internal(608 &sender,609 &collection,610 &address,611 true,612 )?;613614 Ok(())615 }616617 618 619 620 621 622 623 624 625 626 627 628 629 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]630 #[transactional]631 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{632633 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);634 let collection = Self::get_collection(collection_id)?;635636 Self::toggle_white_list_internal(637 &sender,638 &collection,639 &address,640 false,641 )?;642643 Ok(())644 }645646 647 648 649 650 651 652 653 654 655 656 657 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]658 #[transactional]659 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult660 {661 let sender = ensure_signed(origin)?;662663 let mut target_collection = Self::get_collection(collection_id)?;664 Self::check_owner_permissions(&target_collection, &sender)?;665 target_collection.access = mode;666 Self::save_collection(target_collection);667668 Ok(())669 }670671 672 673 674 675 676 677 678 679 680 681 682 683 684 #[weight = <T as Config>::WeightInfo::set_mint_permission()]685 #[transactional]686 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult687 {688 let sender = ensure_signed(origin)?;689690 let mut target_collection = Self::get_collection(collection_id)?;691 Self::check_owner_permissions(&target_collection, &sender)?;692 target_collection.mint_mode = mint_permission;693 Self::save_collection(target_collection);694695 Ok(())696 }697698 699 700 701 702 703 704 705 706 707 708 709 #[weight = <T as Config>::WeightInfo::change_collection_owner()]710 #[transactional]711 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {712713 let sender = ensure_signed(origin)?;714 let mut target_collection = Self::get_collection(collection_id)?;715 Self::check_owner_permissions(&target_collection, &sender)?;716 target_collection.owner = new_owner;717 Self::save_collection(target_collection);718719 Ok(())720 }721722 723 724 725 726 727 728 729 730 731 732 733 734 735 #[weight = <T as Config>::WeightInfo::add_collection_admin()]736 #[transactional]737 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {738 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);739 let collection = Self::get_collection(collection_id)?;740 Self::check_owner_or_admin_permissions(&collection, &sender)?;741 let mut admin_arr = <AdminList<T>>::get(collection_id);742743 match admin_arr.binary_search(&new_admin_id) {744 Ok(_) => {},745 Err(idx) => {746 let limits = ChainLimit::get();747 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);748 admin_arr.insert(idx, new_admin_id);749 <AdminList<T>>::insert(collection_id, admin_arr);750 }751 }752 Ok(())753 }754755 756 757 758 759 760 761 762 763 764 765 766 767 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]768 #[transactional]769 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {770 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);771 let collection = Self::get_collection(collection_id)?;772 Self::check_owner_or_admin_permissions(&collection, &sender)?;773 let mut admin_arr = <AdminList<T>>::get(collection_id);774775 match admin_arr.binary_search(&account_id) {776 Ok(idx) => {777 admin_arr.remove(idx);778 <AdminList<T>>::insert(collection_id, admin_arr);779 },780 Err(_) => {}781 }782 Ok(())783 }784785 786 787 788 789 790 791 792 793 794 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]795 #[transactional]796 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {797 let sender = ensure_signed(origin)?;798 let mut target_collection = Self::get_collection(collection_id)?;799 Self::check_owner_permissions(&target_collection, &sender)?;800801 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);802 Self::save_collection(target_collection);803804 Ok(())805 }806807 808 809 810 811 812 813 814 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]815 #[transactional]816 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {817 let sender = ensure_signed(origin)?;818819 let mut target_collection = Self::get_collection(collection_id)?;820 ensure!(821 target_collection.sponsorship.pending_sponsor() == Some(&sender),822 Error::<T>::ConfirmUnsetSponsorFail823 );824825 target_collection.sponsorship = SponsorshipState::Confirmed(sender);826 Self::save_collection(target_collection);827828 Ok(())829 }830831 832 833 834 835 836 837 838 839 840 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]841 #[transactional]842 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {843 let sender = ensure_signed(origin)?;844845 let mut target_collection = Self::get_collection(collection_id)?;846 Self::check_owner_permissions(&target_collection, &sender)?;847848 target_collection.sponsorship = SponsorshipState::Disabled;849 Self::save_collection(target_collection);850851 Ok(())852 }853854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877878 #[weight = <T as Config>::WeightInfo::create_item(data.len())]879 #[transactional]880 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {881 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);882 let collection = Self::get_collection(collection_id)?;883884 Self::create_item_internal(&sender, &collection, &owner, data)?;885886 Self::submit_logs(collection)?;887 Ok(())888 }889890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()909 .map(|data| { data.len() })910 .sum())]911 #[transactional]912 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {913914 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 let collection = Self::get_collection(collection_id)?;917918 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;919920 Self::submit_logs(collection)?;921 Ok(())922 }923924 925 926 927 928 929 930 931 932 933 934 935 936 937 #[weight = <T as Config>::WeightInfo::burn_item()]938 #[transactional]939 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {940941 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);942 let target_collection = Self::get_collection(collection_id)?;943944 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;945946 Self::submit_logs(target_collection)?;947 Ok(())948 }949950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 #[weight = <T as Config>::WeightInfo::transfer()]974 #[transactional]975 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {976 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);977 let collection = Self::get_collection(collection_id)?;978979 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;980981 Self::submit_logs(collection)?;982 Ok(())983 }984985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 #[weight = <T as Config>::WeightInfo::approve()]1001 #[transactional]1002 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1003 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1004 let collection = Self::get_collection(collection_id)?;10051006 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10071008 Self::submit_logs(collection)?;1009 Ok(())1010 }1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 #[weight = <T as Config>::WeightInfo::transfer_from()]1032 #[transactional]1033 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1034 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1035 let collection = Self::get_collection(collection_id)?;10361037 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10381039 Self::submit_logs(collection)?;1040 Ok(())1041 }1042 1043 1044 1045 1046 10471048 10491050 10511052 1053 10541055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1068 #[transactional]1069 pub fn set_variable_meta_data (1070 origin,1071 collection_id: CollectionId,1072 item_id: TokenId,1073 data: Vec<u8>1074 ) -> DispatchResult {1075 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1076 1077 let collection = Self::get_collection(collection_id)?;10781079 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10801081 Ok(())1082 }1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 #[weight = <T as Config>::WeightInfo::set_schema_version()]1099 #[transactional]1100 pub fn set_schema_version(1101 origin,1102 collection_id: CollectionId,1103 version: SchemaVersion1104 ) -> DispatchResult {1105 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1106 let mut target_collection = Self::get_collection(collection_id)?;1107 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1108 target_collection.schema_version = version;1109 Self::save_collection(target_collection);11101111 Ok(())1112 }11131114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1127 #[transactional]1128 pub fn set_offchain_schema(1129 origin,1130 collection_id: CollectionId,1131 schema: Vec<u8>1132 ) -> DispatchResult {1133 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1134 let mut target_collection = Self::get_collection(collection_id)?;1135 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11361137 1138 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11391140 target_collection.offchain_schema = schema;1141 Self::save_collection(target_collection);11421143 Ok(())1144 }11451146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1159 #[transactional]1160 pub fn set_const_on_chain_schema (1161 origin,1162 collection_id: CollectionId,1163 schema: Vec<u8>1164 ) -> DispatchResult {1165 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1166 let mut target_collection = Self::get_collection(collection_id)?;1167 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11681169 1170 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11711172 target_collection.const_on_chain_schema = schema;1173 Self::save_collection(target_collection);11741175 Ok(())1176 }11771178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1191 #[transactional]1192 pub fn set_variable_on_chain_schema (1193 origin,1194 collection_id: CollectionId,1195 schema: Vec<u8>1196 ) -> DispatchResult {1197 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1198 let mut target_collection = Self::get_collection(collection_id)?;1199 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12001201 1202 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12031204 target_collection.variable_on_chain_schema = schema;1205 Self::save_collection(target_collection);12061207 Ok(())1208 }12091210 1211 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1212 #[transactional]1213 pub fn set_chain_limits(1214 origin,1215 limits: ChainLimits1216 ) -> DispatchResult {12171218 #[cfg(not(feature = "runtime-benchmarks"))]1219 ensure_root(origin)?;12201221 <ChainLimit>::put(limits);1222 Ok(())1223 }12241225 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1226 #[transactional]1227 pub fn set_collection_limits(1228 origin,1229 collection_id: u32,1230 new_limits: CollectionLimits<T::BlockNumber>,1231 ) -> DispatchResult {1232 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1233 let mut target_collection = Self::get_collection(collection_id)?;1234 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1235 let old_limits = &target_collection.limits;1236 let chain_limits = ChainLimit::get();12371238 1239 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1240 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1241 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1242 Error::<T>::CollectionLimitBoundsExceeded);12431244 1245 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1246 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12471248 ensure!(1249 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1250 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1251 Error::<T>::OwnerPermissionsCantBeReverted,1252 );12531254 target_collection.limits = new_limits;1255 Self::save_collection(target_collection);12561257 Ok(())1258 } 1259 }1260}12611262impl<T: Config> Module<T> {1263 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1264 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1265 Self::validate_create_item_args(&collection, &data)?;1266 Self::create_item_no_validation(&collection, owner, data)?;12671268 Ok(())1269 }12701271 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1272 target_collection.consume_gas(2000000)?;1273 1274 Self::is_correct_transfer(target_collection, &recipient)?;12751276 1277 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1278 Self::is_owner_or_admin_permissions(target_collection, &sender),1279 Error::<T>::NoPermission);12801281 if target_collection.access == AccessMode::WhiteList {1282 Self::check_white_list(target_collection, &sender)?;1283 Self::check_white_list(target_collection, &recipient)?;1284 }12851286 match target_collection.mode1287 {1288 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1289 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1290 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1291 _ => ()1292 };12931294 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));12951296 Ok(())1297 }12981299 pub fn approve_internal(1300 sender: &T::CrossAccountId,1301 spender: &T::CrossAccountId,1302 collection: &CollectionHandle<T>,1303 item_id: TokenId,1304 amount: u1281305 ) -> DispatchResult {1306 collection.consume_gas(2000000)?;1307 Self::token_exists(&collection, item_id)?;13081309 1310 let bypasses_limits = collection.limits.owner_can_transfer &&1311 Self::is_owner_or_admin_permissions(1312 &collection,1313 &sender,1314 );13151316 let allowance_limit = if bypasses_limits {1317 None1318 } else if let Some(amount) = Self::owned_amount(1319 &sender,1320 &collection,1321 item_id,1322 ) {1323 Some(amount)1324 } else {1325 fail!(Error::<T>::NoPermission);1326 };13271328 if collection.access == AccessMode::WhiteList {1329 Self::check_white_list(&collection, &sender)?;1330 Self::check_white_list(&collection, &spender)?;1331 }13321333 let allowance: u128 = amount1334 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1335 .ok_or(Error::<T>::NumOverflow)?;1336 if let Some(limit) = allowance_limit {1337 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1338 }1339 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);13401341 if matches!(collection.mode, CollectionMode::NFT) {1342 1343 collection.log(ERC721Events::Approval {1344 owner: *sender.as_eth(),1345 approved: *spender.as_eth(),1346 token_id: item_id.into(),1347 });1348 }13491350 if matches!(collection.mode, CollectionMode::Fungible(_)) {1351 1352 collection.log(ERC20Events::Approval {1353 owner: *sender.as_eth(),1354 spender: *spender.as_eth(),1355 value: allowance.into()1356 });1357 }13581359 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1360 Ok(())1361 }13621363 pub fn transfer_from_internal(1364 sender: &T::CrossAccountId,1365 from: &T::CrossAccountId,1366 recipient: &T::CrossAccountId,1367 collection: &CollectionHandle<T>,1368 item_id: TokenId,1369 amount: u128,1370 ) -> DispatchResult {1371 collection.consume_gas(2000000)?;1372 1373 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13741375 1376 Self::is_correct_transfer(&collection, &recipient)?;13771378 1379 ensure!(1380 approval >= amount || 1381 (1382 collection.limits.owner_can_transfer &&1383 Self::is_owner_or_admin_permissions(&collection, &sender)1384 ),1385 Error::<T>::NoPermission1386 );13871388 if collection.access == AccessMode::WhiteList {1389 Self::check_white_list(&collection, &sender)?;1390 Self::check_white_list(&collection, &recipient)?;1391 }13921393 1394 let allowance = approval.saturating_sub(amount);1395 if allowance > 0 {1396 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1397 } else {1398 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1399 }14001401 match collection.mode {1402 CollectionMode::NFT => {1403 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1404 }1405 CollectionMode::Fungible(_) => {1406 Self::transfer_fungible(&collection, amount, &from, &recipient)?1407 }1408 CollectionMode::ReFungible => {1409 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1410 }1411 _ => ()1412 };14131414 if matches!(collection.mode, CollectionMode::Fungible(_)) {1415 collection.log(ERC20Events::Approval {1416 owner: *from.as_eth(),1417 spender: *sender.as_eth(),1418 value: allowance.into()1419 });1420 }14211422 Ok(())1423 }14241425 pub fn set_variable_meta_data_internal(1426 sender: &T::CrossAccountId,1427 collection: &CollectionHandle<T>, 1428 item_id: TokenId,1429 data: Vec<u8>,1430 ) -> DispatchResult {1431 Self::token_exists(&collection, item_id)?;14321433 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);14341435 1436 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1437 Self::is_owner_or_admin_permissions(&collection, &sender),1438 Error::<T>::NoPermission);14391440 match collection.mode1441 {1442 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1443 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1444 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1445 _ => fail!(Error::<T>::UnexpectedCollectionType)1446 };14471448 Ok(())1449 }14501451 pub fn create_multiple_items_internal(1452 sender: &T::CrossAccountId,1453 collection: &CollectionHandle<T>,1454 owner: &T::CrossAccountId,1455 items_data: Vec<CreateItemData>,1456 ) -> DispatchResult {1457 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;14581459 for data in &items_data {1460 Self::validate_create_item_args(&collection, data)?;1461 }1462 for data in &items_data {1463 Self::create_item_no_validation(&collection, owner, data.clone())?;1464 }14651466 Ok(())1467 }14681469 pub fn burn_item_internal(1470 sender: &T::CrossAccountId,1471 collection: &CollectionHandle<T>,1472 item_id: TokenId,1473 value: u128,1474 ) -> DispatchResult {1475 ensure!(1476 Self::is_item_owner(&sender, &collection, item_id) ||1477 (1478 collection.limits.owner_can_transfer &&1479 Self::is_owner_or_admin_permissions(&collection, &sender)1480 ),1481 Error::<T>::NoPermission1482 );14831484 if collection.access == AccessMode::WhiteList {1485 Self::check_white_list(&collection, &sender)?;1486 }14871488 match collection.mode1489 {1490 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1491 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1492 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1493 _ => ()1494 };14951496 Ok(())1497 }14981499 pub fn toggle_white_list_internal(1500 sender: &T::CrossAccountId,1501 collection: &CollectionHandle<T>,1502 address: &T::CrossAccountId,1503 whitelisted: bool,1504 ) -> DispatchResult {1505 Self::check_owner_or_admin_permissions(&collection, &sender)?;15061507 if whitelisted {1508 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1509 } else {1510 <WhiteList<T>>::remove(collection.id, address.as_sub());1511 }15121513 Ok(())1514 }15151516 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1517 let collection_id = collection.id;15181519 1520 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1521 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1522 1523 Ok(())1524 }15251526 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1527 let collection_id = collection.id;15281529 1530 let total_items: u32 = ItemListIndex::get(collection_id)1531 .checked_add(amount)1532 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1533 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1534 .checked_add(amount)1535 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1536 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1537 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);15381539 if !Self::is_owner_or_admin_permissions(collection, &sender) {1540 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1541 Self::check_white_list(collection, owner)?;1542 Self::check_white_list(collection, sender)?;1543 }15441545 Ok(())1546 }15471548 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1549 match target_collection.mode1550 {1551 CollectionMode::NFT => {1552 if let CreateItemData::NFT(data) = data {1553 1554 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1555 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1556 } else {1557 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1558 }1559 },1560 CollectionMode::Fungible(_) => {1561 if let CreateItemData::Fungible(_) = data {1562 } else {1563 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1564 }1565 },1566 CollectionMode::ReFungible => {1567 if let CreateItemData::ReFungible(data) = data {15681569 1570 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1571 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15721573 1574 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1575 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1576 } else {1577 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1578 }1579 },1580 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1581 };15821583 Ok(())1584 }15851586 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1587 match data1588 {1589 CreateItemData::NFT(data) => {1590 let item = NftItemType {1591 owner: owner.clone(),1592 const_data: data.const_data,1593 variable_data: data.variable_data1594 };15951596 Self::add_nft_item(collection, item)?;1597 },1598 CreateItemData::Fungible(data) => {1599 Self::add_fungible_item(collection, &owner, data.value)?;1600 },1601 CreateItemData::ReFungible(data) => {1602 let mut owner_list = Vec::new();1603 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16041605 let item = ReFungibleItemType {1606 owner: owner_list,1607 const_data: data.const_data,1608 variable_data: data.variable_data1609 };16101611 Self::add_refungible_item(collection, item)?;1612 }1613 };16141615 Ok(())1616 }16171618 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1619 let collection_id = collection.id;16201621 1622 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16231624 1625 let item = FungibleItemType {1626 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1627 };1628 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16291630 1631 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1632 .checked_add(value)1633 .ok_or(Error::<T>::NumOverflow)?;1634 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16351636 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1637 Ok(())1638 }16391640 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1641 let collection_id = collection.id;16421643 let current_index = <ItemListIndex>::get(collection_id)1644 .checked_add(1)1645 .ok_or(Error::<T>::NumOverflow)?;1646 let itemcopy = item.clone();16471648 ensure!(1649 item.owner.len() == 1,1650 Error::<T>::BadCreateRefungibleCall,1651 );1652 let item_owner = item.owner.first().expect("only one owner is defined");16531654 let value = item_owner.fraction;1655 let owner = item_owner.owner.clone();16561657 Self::add_token_index(collection_id, current_index, &owner)?;16581659 <ItemListIndex>::insert(collection_id, current_index);1660 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16611662 1663 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1664 .checked_add(value)1665 .ok_or(Error::<T>::NumOverflow)?;1666 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16671668 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1669 Ok(())1670 }16711672 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1673 let collection_id = collection.id;16741675 let current_index = <ItemListIndex>::get(collection_id)1676 .checked_add(1)1677 .ok_or(Error::<T>::NumOverflow)?;16781679 let item_owner = item.owner.clone();1680 Self::add_token_index(collection_id, current_index, &item.owner)?;16811682 <ItemListIndex>::insert(collection_id, current_index);1683 <NftItemList<T>>::insert(collection_id, current_index, item);16841685 1686 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1687 .checked_add(1)1688 .ok_or(Error::<T>::NumOverflow)?;1689 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);16901691 collection.log(ERC721Events::Transfer {1692 from: H160::default(),1693 to: *item_owner.as_eth(),1694 token_id: current_index.into(),1695 });1696 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1697 Ok(())1698 }16991700 fn burn_refungible_item(1701 collection: &CollectionHandle<T>,1702 item_id: TokenId,1703 owner: &T::CrossAccountId,1704 ) -> DispatchResult {1705 let collection_id = collection.id;17061707 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1708 .ok_or(Error::<T>::TokenNotFound)?;1709 let rft_balance = token1710 .owner1711 .iter()1712 .find(|&i| i.owner == *owner)1713 .ok_or(Error::<T>::TokenNotFound)?;1714 Self::remove_token_index(collection_id, item_id, owner)?;17151716 1717 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1718 .checked_sub(rft_balance.fraction)1719 .ok_or(Error::<T>::NumOverflow)?;1720 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17211722 1723 let index = token1724 .owner1725 .iter()1726 .position(|i| i.owner == *owner)1727 .expect("owned item is exists");1728 token.owner.remove(index);1729 let owner_count = token.owner.len();17301731 1732 if owner_count == 0 {1733 <ReFungibleItemList<T>>::remove(collection_id, item_id);1734 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1735 }1736 else {1737 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1738 }17391740 Ok(())1741 }17421743 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1744 let collection_id = collection.id;17451746 let item = <NftItemList<T>>::get(collection_id, item_id)1747 .ok_or(Error::<T>::TokenNotFound)?;1748 Self::remove_token_index(collection_id, item_id, &item.owner)?;17491750 1751 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1752 .checked_sub(1)1753 .ok_or(Error::<T>::NumOverflow)?;1754 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1755 <NftItemList<T>>::remove(collection_id, item_id);1756 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17571758 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1759 Ok(())1760 }17611762 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1763 let collection_id = collection.id;17641765 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1766 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17671768 1769 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1770 .checked_sub(value)1771 .ok_or(Error::<T>::NumOverflow)?;1772 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17731774 if balance.value - value > 0 {1775 balance.value -= value;1776 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1777 }1778 else {1779 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1780 }17811782 collection.log(ERC20Events::Transfer {1783 from: *owner.as_eth(),1784 to: H160::default(),1785 value: value.into(),1786 });1787 Ok(())1788 }17891790 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1791 Ok(<CollectionHandle<T>>::get(collection_id)1792 .ok_or(Error::<T>::CollectionNotFound)?)1793 }17941795 fn save_collection(collection: CollectionHandle<T>) {1796 <CollectionById<T>>::insert(collection.id, collection.into_inner());1797 }17981799 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1800 if collection.logs.is_empty() {1801 return Ok(())1802 }1803 T::EthereumTransactionSender::submit_logs_transaction(1804 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1805 collection.logs.retrieve_logs(),1806 )1807 }18081809 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1810 ensure!(1811 *subject == target_collection.owner,1812 Error::<T>::NoPermission1813 );18141815 Ok(())1816 }18171818 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1819 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1820 }18211822 fn check_owner_or_admin_permissions(1823 collection: &CollectionHandle<T>,1824 subject: &T::CrossAccountId,1825 ) -> DispatchResult {1826 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18271828 Ok(())1829 }18301831 fn owned_amount(1832 subject: &T::CrossAccountId,1833 target_collection: &CollectionHandle<T>,1834 item_id: TokenId,1835 ) -> Option<u128> {1836 let collection_id = target_collection.id;18371838 match target_collection.mode {1839 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)1840 .then(|| 1),1841 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())1842 .value),1843 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1844 .owner1845 .iter()1846 .find(|i| i.owner == *subject)1847 .map(|i| i.fraction),1848 CollectionMode::Invalid => None,1849 }1850 }18511852 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1853 match target_collection.mode {1854 CollectionMode::Fungible(_) => true,1855 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),1856 }1857 }18581859 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {1860 let collection_id = collection.id;18611862 let mes = Error::<T>::AddresNotInWhiteList;1863 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);18641865 Ok(())1866 }18671868 1869 1870 fn token_exists(1871 target_collection: &CollectionHandle<T>,1872 item_id: TokenId,1873 ) -> DispatchResult {1874 let collection_id = target_collection.id;1875 let exists = match target_collection.mode1876 {1877 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1878 CollectionMode::Fungible(_) => true,1879 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1880 _ => false1881 };18821883 ensure!(exists == true, Error::<T>::TokenNotFound);1884 Ok(())1885 }18861887 fn transfer_fungible(1888 collection: &CollectionHandle<T>,1889 value: u128,1890 owner: &T::CrossAccountId,1891 recipient: &T::CrossAccountId,1892 ) -> DispatchResult {1893 let collection_id = collection.id;18941895 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1896 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18971898 1899 Self::add_fungible_item(collection, recipient, value)?;19001901 1902 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19031904 1905 if balance.value == value {1906 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1907 }1908 else {1909 balance.value -= value;1910 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1911 }19121913 collection.log(ERC20Events::Transfer {1914 from: *owner.as_eth(),1915 to: *recipient.as_eth(),1916 value: value.into(),1917 });1918 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));19191920 Ok(())1921 }19221923 fn transfer_refungible(1924 collection: &CollectionHandle<T>,1925 item_id: TokenId,1926 value: u128,1927 owner: T::CrossAccountId,1928 new_owner: T::CrossAccountId,1929 ) -> DispatchResult {1930 let collection_id = collection.id;1931 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1932 .ok_or(Error::<T>::TokenNotFound)?;19331934 let item = full_item1935 .owner1936 .iter()1937 .filter(|i| i.owner == owner)1938 .next()1939 .ok_or(Error::<T>::TokenNotFound)?;1940 let amount = item.fraction;19411942 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19431944 1945 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())1946 .checked_sub(value)1947 .ok_or(Error::<T>::NumOverflow)?;1948 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);19491950 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())1951 .checked_add(value)1952 .ok_or(Error::<T>::NumOverflow)?;1953 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);19541955 let old_owner = item.owner.clone();1956 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19571958 1959 if amount == value && !new_owner_has_account {1960 1961 1962 let mut new_full_item = full_item.clone();1963 new_full_item1964 .owner1965 .iter_mut()1966 .find(|i| i.owner == owner)1967 .expect("old owner does present in refungible")1968 .owner = new_owner.clone();1969 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19701971 1972 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1973 } else {1974 let mut new_full_item = full_item.clone();1975 new_full_item1976 .owner1977 .iter_mut()1978 .find(|i| i.owner == owner)1979 .expect("old owner does present in refungible")1980 .fraction -= value;19811982 1983 if new_owner_has_account {1984 1985 new_full_item1986 .owner1987 .iter_mut()1988 .find(|i| i.owner == new_owner)1989 .expect("new owner has account")1990 .fraction += value;1991 } else {1992 1993 new_full_item.owner.push(Ownership {1994 owner: new_owner.clone(),1995 fraction: value,1996 });1997 Self::add_token_index(collection_id, item_id, &new_owner)?;1998 }19992000 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2001 }20022003 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));20042005 Ok(())2006 }20072008 fn transfer_nft(2009 collection: &CollectionHandle<T>,2010 item_id: TokenId,2011 sender: T::CrossAccountId,2012 new_owner: T::CrossAccountId,2013 ) -> DispatchResult {2014 let collection_id = collection.id;2015 let mut item = <NftItemList<T>>::get(collection_id, item_id)2016 .ok_or(Error::<T>::TokenNotFound)?;20172018 ensure!(2019 sender == item.owner,2020 Error::<T>::MustBeTokenOwner2021 );20222023 2024 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2025 .checked_sub(1)2026 .ok_or(Error::<T>::NumOverflow)?;2027 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20282029 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2030 .checked_add(1)2031 .ok_or(Error::<T>::NumOverflow)?;2032 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20332034 2035 let old_owner = item.owner.clone();2036 item.owner = new_owner.clone();2037 <NftItemList<T>>::insert(collection_id, item_id, item);20382039 2040 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20412042 collection.log(ERC721Events::Transfer {2043 from: *sender.as_eth(),2044 to: *new_owner.as_eth(),2045 token_id: item_id.into(),2046 });2047 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));20482049 Ok(())2050 }2051 2052 fn set_re_fungible_variable_data(2053 collection: &CollectionHandle<T>,2054 item_id: TokenId,2055 data: Vec<u8>2056 ) -> DispatchResult {2057 let collection_id = collection.id;2058 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2059 .ok_or(Error::<T>::TokenNotFound)?;20602061 item.variable_data = data;20622063 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20642065 Ok(())2066 }20672068 fn set_nft_variable_data(2069 collection: &CollectionHandle<T>,2070 item_id: TokenId,2071 data: Vec<u8>2072 ) -> DispatchResult {2073 let collection_id = collection.id;2074 let mut item = <NftItemList<T>>::get(collection_id, item_id)2075 .ok_or(Error::<T>::TokenNotFound)?;2076 2077 item.variable_data = data;20782079 <NftItemList<T>>::insert(collection_id, item_id, item);2080 2081 Ok(())2082 }20832084 #[allow(dead_code)]2085 fn init_collection(item: &Collection<T>) {2086 2087 assert!(2088 item.decimal_points <= MAX_DECIMAL_POINTS,2089 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2090 );2091 assert!(2092 item.name.len() <= 64,2093 "Collection name can not be longer than 63 char"2094 );2095 assert!(2096 item.name.len() <= 256,2097 "Collection description can not be longer than 255 char"2098 );2099 assert!(2100 item.token_prefix.len() <= 16,2101 "Token prefix can not be longer than 15 char"2102 );21032104 2105 let next_id = CreatedCollectionCount::get()2106 .checked_add(1)2107 .unwrap();21082109 CreatedCollectionCount::put(next_id);2110 }21112112 #[allow(dead_code)]2113 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2114 let current_index = <ItemListIndex>::get(collection_id)2115 .checked_add(1)2116 .unwrap();21172118 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21192120 <ItemListIndex>::insert(collection_id, current_index);21212122 2123 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2124 .checked_add(1)2125 .unwrap();2126 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2127 }21282129 #[allow(dead_code)]2130 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2131 let current_index = <ItemListIndex>::get(collection_id)2132 .checked_add(1)2133 .unwrap();21342135 Self::add_token_index(collection_id, current_index, owner).unwrap();21362137 <ItemListIndex>::insert(collection_id, current_index);21382139 2140 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2141 .checked_add(item.value)2142 .unwrap();2143 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2144 }21452146 #[allow(dead_code)]2147 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2148 let current_index = <ItemListIndex>::get(collection_id)2149 .checked_add(1)2150 .unwrap();21512152 let value = item.owner.first().unwrap().fraction;2153 let owner = item.owner.first().unwrap().owner.clone();21542155 Self::add_token_index(collection_id, current_index, &owner).unwrap();21562157 <ItemListIndex>::insert(collection_id, current_index);21582159 2160 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2161 .checked_add(value)2162 .unwrap();2163 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2164 }21652166 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2167 2168 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {21692170 2171 let count = <AccountItemCount<T>>::get(owner.as_sub());2172 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21732174 <AccountItemCount<T>>::insert(owner.as_sub(), count2175 .checked_add(1)2176 .ok_or(Error::<T>::NumOverflow)?);2177 }2178 else {2179 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2180 }21812182 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2183 if list_exists {2184 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2185 let item_contains = list.contains(&item_index.clone());21862187 if !item_contains {2188 list.push(item_index.clone());2189 }21902191 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2192 } else {2193 let mut itm = Vec::new();2194 itm.push(item_index.clone());2195 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2196 }21972198 Ok(())2199 }22002201 fn remove_token_index(2202 collection_id: CollectionId,2203 item_index: TokenId,2204 owner: &T::CrossAccountId,2205 ) -> DispatchResult {22062207 2208 <AccountItemCount<T>>::insert(owner.as_sub(), 2209 <AccountItemCount<T>>::get(owner.as_sub())2210 .checked_sub(1)2211 .ok_or(Error::<T>::NumOverflow)?);221222132214 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2215 if list_exists {2216 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2217 let item_contains = list.contains(&item_index.clone());22182219 if item_contains {2220 list.retain(|&item| item != item_index);2221 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2222 }2223 }22242225 Ok(())2226 }22272228 fn move_token_index(2229 collection_id: CollectionId,2230 item_index: TokenId,2231 old_owner: &T::CrossAccountId,2232 new_owner: &T::CrossAccountId,2233 ) -> DispatchResult {2234 Self::remove_token_index(collection_id, item_index, old_owner)?;2235 Self::add_token_index(collection_id, item_index, new_owner)?;22362237 Ok(())2238 }2239}22402241sp_api::decl_runtime_apis! {2242 pub trait NftApi {2243 2244 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2245 }2246}