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 add_extra_genesis {348 build(|config: &GenesisConfig<T>| {349 350 for (_num, _c) in &config.collection_id {351 <Module<T>>::init_collection(_c);352 }353354 for (_num, _c, _i) in &config.nft_item_id {355 <Module<T>>::init_nft_token(*_c, _i);356 }357358 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {359 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);360 }361362 for (_num, _c, _i) in &config.refungible_item_id {363 <Module<T>>::init_refungible_token(*_c, _i);364 }365 })366 }367}368369decl_event!(370 pub enum Event<T>371 where372 AccountId = <T as frame_system::Config>::AccountId,373 CrossAccountId = <T as Config>::CrossAccountId,374 {375 376 377 378 379 380 381 382 383 384 CollectionCreated(CollectionId, u8, AccountId),385386 387 388 389 390 391 392 393 394 395 ItemCreated(CollectionId, TokenId, CrossAccountId),396397 398 399 400 401 402 403 404 ItemDestroyed(CollectionId, TokenId),405406 407 408 409 410 411 412 413 414 415 416 417 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),418419 420 421 422 423 424 425 426 427 428 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),429 }430);431432decl_module! {433 pub struct Module<T: Config> for enum Call 434 where 435 origin: T::Origin436 {437 fn deposit_event() = default;438 type Error = Error<T>;439440 fn on_initialize(_now: T::BlockNumber) -> Weight {441 0442 }443444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 #[weight = <T as Config>::WeightInfo::create_collection()]461 #[transactional]462 pub fn create_collection(origin,463 collection_name: Vec<u16>,464 collection_description: Vec<u16>,465 token_prefix: Vec<u8>,466 mode: CollectionMode) -> DispatchResult {467468 469 let who = ensure_signed(origin)?;470471 472 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();473 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(474 &T::TreasuryAccountId::get(),475 T::CollectionCreationPrice::get(),476 ));477 <T as Config>::Currency::settle(478 &who,479 imbalance,480 WithdrawReasons::TRANSFER,481 ExistenceRequirement::KeepAlive,482 ).map_err(|_| Error::<T>::NoPermission)?;483484 let decimal_points = match mode {485 CollectionMode::Fungible(points) => points,486 _ => 0487 };488489 let chain_limit = ChainLimit::get();490491 let created_count = CreatedCollectionCount::get();492 let destroyed_count = DestroyedCollectionCount::get();493494 495 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497 498 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);499 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);500 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);501 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);502503 504 let next_id = created_count505 .checked_add(1)506 .ok_or(Error::<T>::NumOverflow)?;507508 CreatedCollectionCount::put(next_id);509510 let limits = CollectionLimits {511 sponsored_data_size: chain_limit.custom_data_limit,512 ..Default::default()513 };514515 516 let new_collection = Collection {517 owner: who.clone(),518 name: collection_name,519 mode: mode.clone(),520 mint_mode: false,521 access: AccessMode::Normal,522 description: collection_description,523 decimal_points: decimal_points,524 token_prefix: token_prefix,525 offchain_schema: Vec::new(),526 schema_version: SchemaVersion::ImageURL,527 sponsorship: SponsorshipState::Disabled,528 variable_on_chain_schema: Vec::new(),529 const_on_chain_schema: Vec::new(),530 limits,531 };532533 534 <CollectionById<T>>::insert(next_id, new_collection);535536 537 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));538539 Ok(())540 }541542 543 544 545 546 547 548 549 550 551 #[weight = <T as Config>::WeightInfo::destroy_collection()]552 #[transactional]553 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {554555 let sender = ensure_signed(origin)?;556 let collection = Self::get_collection(collection_id)?;557 Self::check_owner_permissions(&collection, &sender)?;558 if !collection.limits.owner_can_destroy {559 fail!(Error::<T>::NoPermission);560 }561562 <AddressTokens<T>>::remove_prefix(collection_id);563 <Allowances<T>>::remove_prefix(collection_id);564 <Balance<T>>::remove_prefix(collection_id);565 <ItemListIndex>::remove(collection_id);566 <AdminList<T>>::remove(collection_id);567 <CollectionById<T>>::remove(collection_id);568 <WhiteList<T>>::remove_prefix(collection_id);569570 <NftItemList<T>>::remove_prefix(collection_id);571 <FungibleItemList<T>>::remove_prefix(collection_id);572 <ReFungibleItemList<T>>::remove_prefix(collection_id);573574 <NftTransferBasket<T>>::remove_prefix(collection_id);575 <FungibleTransferBasket<T>>::remove_prefix(collection_id);576 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);577578 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);579580 DestroyedCollectionCount::put(DestroyedCollectionCount::get()581 .checked_add(1)582 .ok_or(Error::<T>::NumOverflow)?);583584 Ok(())585 }586587 588 589 590 591 592 593 594 595 596 597 598 599 #[weight = <T as Config>::WeightInfo::add_to_white_list()]600 #[transactional]601 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{602603 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);604 let collection = Self::get_collection(collection_id)?;605606 Self::toggle_white_list_internal(607 &sender,608 &collection,609 &address,610 true,611 )?;612613 Ok(())614 }615616 617 618 619 620 621 622 623 624 625 626 627 628 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]629 #[transactional]630 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{631632 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633 let collection = Self::get_collection(collection_id)?;634635 Self::toggle_white_list_internal(636 &sender,637 &collection,638 &address,639 false,640 )?;641642 Ok(())643 }644645 646 647 648 649 650 651 652 653 654 655 656 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]657 #[transactional]658 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult659 {660 let sender = ensure_signed(origin)?;661662 let mut target_collection = Self::get_collection(collection_id)?;663 Self::check_owner_permissions(&target_collection, &sender)?;664 target_collection.access = mode;665 Self::save_collection(target_collection);666667 Ok(())668 }669670 671 672 673 674 675 676 677 678 679 680 681 682 683 #[weight = <T as Config>::WeightInfo::set_mint_permission()]684 #[transactional]685 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult686 {687 let sender = ensure_signed(origin)?;688689 let mut target_collection = Self::get_collection(collection_id)?;690 Self::check_owner_permissions(&target_collection, &sender)?;691 target_collection.mint_mode = mint_permission;692 Self::save_collection(target_collection);693694 Ok(())695 }696697 698 699 700 701 702 703 704 705 706 707 708 #[weight = <T as Config>::WeightInfo::change_collection_owner()]709 #[transactional]710 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {711712 let sender = ensure_signed(origin)?;713 let mut target_collection = Self::get_collection(collection_id)?;714 Self::check_owner_permissions(&target_collection, &sender)?;715 target_collection.owner = new_owner;716 Self::save_collection(target_collection);717718 Ok(())719 }720721 722 723 724 725 726 727 728 729 730 731 732 733 734 #[weight = <T as Config>::WeightInfo::add_collection_admin()]735 #[transactional]736 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {737 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);738 let collection = Self::get_collection(collection_id)?;739 Self::check_owner_or_admin_permissions(&collection, &sender)?;740 let mut admin_arr = <AdminList<T>>::get(collection_id);741742 match admin_arr.binary_search(&new_admin_id) {743 Ok(_) => {},744 Err(idx) => {745 let limits = ChainLimit::get();746 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);747 admin_arr.insert(idx, new_admin_id);748 <AdminList<T>>::insert(collection_id, admin_arr);749 }750 }751 Ok(())752 }753754 755 756 757 758 759 760 761 762 763 764 765 766 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]767 #[transactional]768 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {769 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);770 let collection = Self::get_collection(collection_id)?;771 Self::check_owner_or_admin_permissions(&collection, &sender)?;772 let mut admin_arr = <AdminList<T>>::get(collection_id);773774 match admin_arr.binary_search(&account_id) {775 Ok(idx) => {776 admin_arr.remove(idx);777 <AdminList<T>>::insert(collection_id, admin_arr);778 },779 Err(_) => {}780 }781 Ok(())782 }783784 785 786 787 788 789 790 791 792 793 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]794 #[transactional]795 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {796 let sender = ensure_signed(origin)?;797 let mut target_collection = Self::get_collection(collection_id)?;798 Self::check_owner_permissions(&target_collection, &sender)?;799800 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);801 Self::save_collection(target_collection);802803 Ok(())804 }805806 807 808 809 810 811 812 813 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]814 #[transactional]815 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {816 let sender = ensure_signed(origin)?;817818 let mut target_collection = Self::get_collection(collection_id)?;819 ensure!(820 target_collection.sponsorship.pending_sponsor() == Some(&sender),821 Error::<T>::ConfirmUnsetSponsorFail822 );823824 target_collection.sponsorship = SponsorshipState::Confirmed(sender);825 Self::save_collection(target_collection);826827 Ok(())828 }829830 831 832 833 834 835 836 837 838 839 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]840 #[transactional]841 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {842 let sender = ensure_signed(origin)?;843844 let mut target_collection = Self::get_collection(collection_id)?;845 Self::check_owner_permissions(&target_collection, &sender)?;846847 target_collection.sponsorship = SponsorshipState::Disabled;848 Self::save_collection(target_collection);849850 Ok(())851 }852853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876877 #[weight = <T as Config>::WeightInfo::create_item(data.len())]878 #[transactional]879 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {880 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);881 let collection = Self::get_collection(collection_id)?;882883 Self::create_item_internal(&sender, &collection, &owner, data)?;884885 Self::submit_logs(collection)?;886 Ok(())887 }888889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()908 .map(|data| { data.len() })909 .sum())]910 #[transactional]911 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {912913 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);914 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915 let collection = Self::get_collection(collection_id)?;916917 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;918919 Self::submit_logs(collection)?;920 Ok(())921 }922923 924 925 926 927 928 929 930 931 932 933 934 935 936 #[weight = <T as Config>::WeightInfo::burn_item()]937 #[transactional]938 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {939940 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);941 let target_collection = Self::get_collection(collection_id)?;942943 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;944945 Self::submit_logs(target_collection)?;946 Ok(())947 }948949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 #[weight = <T as Config>::WeightInfo::transfer()]973 #[transactional]974 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {975 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);976 let collection = Self::get_collection(collection_id)?;977978 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;979980 Self::submit_logs(collection)?;981 Ok(())982 }983984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 #[weight = <T as Config>::WeightInfo::approve()]1000 #[transactional]1001 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1002 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1003 let collection = Self::get_collection(collection_id)?;10041005 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10061007 Self::submit_logs(collection)?;1008 Ok(())1009 }1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 #[weight = <T as Config>::WeightInfo::transfer_from()]1031 #[transactional]1032 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1033 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1034 let collection = Self::get_collection(collection_id)?;10351036 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10371038 Self::submit_logs(collection)?;1039 Ok(())1040 }1041 1042 1043 1044 1045 10461047 10481049 10501051 1052 10531054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1067 #[transactional]1068 pub fn set_variable_meta_data (1069 origin,1070 collection_id: CollectionId,1071 item_id: TokenId,1072 data: Vec<u8>1073 ) -> DispatchResult {1074 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1075 1076 let collection = Self::get_collection(collection_id)?;10771078 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10791080 Ok(())1081 }1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 #[weight = <T as Config>::WeightInfo::set_schema_version()]1098 #[transactional]1099 pub fn set_schema_version(1100 origin,1101 collection_id: CollectionId,1102 version: SchemaVersion1103 ) -> DispatchResult {1104 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1105 let mut target_collection = Self::get_collection(collection_id)?;1106 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1107 target_collection.schema_version = version;1108 Self::save_collection(target_collection);11091110 Ok(())1111 }11121113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1126 #[transactional]1127 pub fn set_offchain_schema(1128 origin,1129 collection_id: CollectionId,1130 schema: Vec<u8>1131 ) -> DispatchResult {1132 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1133 let mut target_collection = Self::get_collection(collection_id)?;1134 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11351136 1137 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11381139 target_collection.offchain_schema = schema;1140 Self::save_collection(target_collection);11411142 Ok(())1143 }11441145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1158 #[transactional]1159 pub fn set_const_on_chain_schema (1160 origin,1161 collection_id: CollectionId,1162 schema: Vec<u8>1163 ) -> DispatchResult {1164 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1165 let mut target_collection = Self::get_collection(collection_id)?;1166 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11671168 1169 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11701171 target_collection.const_on_chain_schema = schema;1172 Self::save_collection(target_collection);11731174 Ok(())1175 }11761177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1190 #[transactional]1191 pub fn set_variable_on_chain_schema (1192 origin,1193 collection_id: CollectionId,1194 schema: Vec<u8>1195 ) -> DispatchResult {1196 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1197 let mut target_collection = Self::get_collection(collection_id)?;1198 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11991200 1201 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12021203 target_collection.variable_on_chain_schema = schema;1204 Self::save_collection(target_collection);12051206 Ok(())1207 }12081209 1210 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1211 #[transactional]1212 pub fn set_chain_limits(1213 origin,1214 limits: ChainLimits1215 ) -> DispatchResult {12161217 #[cfg(not(feature = "runtime-benchmarks"))]1218 ensure_root(origin)?;12191220 <ChainLimit>::put(limits);1221 Ok(())1222 }12231224 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1225 #[transactional]1226 pub fn set_collection_limits(1227 origin,1228 collection_id: u32,1229 new_limits: CollectionLimits<T::BlockNumber>,1230 ) -> DispatchResult {1231 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1232 let mut target_collection = Self::get_collection(collection_id)?;1233 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1234 let old_limits = &target_collection.limits;1235 let chain_limits = ChainLimit::get();12361237 1238 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1239 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1240 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1241 Error::<T>::CollectionLimitBoundsExceeded);12421243 1244 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1245 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12461247 ensure!(1248 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1249 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1250 Error::<T>::OwnerPermissionsCantBeReverted,1251 );12521253 target_collection.limits = new_limits;1254 Self::save_collection(target_collection);12551256 Ok(())1257 } 1258 }1259}12601261impl<T: Config> Module<T> {1262 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1263 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1264 Self::validate_create_item_args(&collection, &data)?;1265 Self::create_item_no_validation(&collection, owner, data)?;12661267 Ok(())1268 }12691270 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1271 target_collection.consume_gas(2000000)?;1272 1273 Self::is_correct_transfer(target_collection, &recipient)?;12741275 1276 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1277 Self::is_owner_or_admin_permissions(target_collection, &sender),1278 Error::<T>::NoPermission);12791280 if target_collection.access == AccessMode::WhiteList {1281 Self::check_white_list(target_collection, &sender)?;1282 Self::check_white_list(target_collection, &recipient)?;1283 }12841285 match target_collection.mode1286 {1287 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1288 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1289 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1290 _ => ()1291 };12921293 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));12941295 Ok(())1296 }12971298 pub fn approve_internal(1299 sender: &T::CrossAccountId,1300 spender: &T::CrossAccountId,1301 collection: &CollectionHandle<T>,1302 item_id: TokenId,1303 amount: u1281304 ) -> DispatchResult {1305 collection.consume_gas(2000000)?;1306 Self::token_exists(&collection, item_id)?;13071308 1309 let bypasses_limits = collection.limits.owner_can_transfer &&1310 Self::is_owner_or_admin_permissions(1311 &collection,1312 &sender,1313 );13141315 let allowance_limit = if bypasses_limits {1316 None1317 } else if let Some(amount) = Self::owned_amount(1318 &sender,1319 &collection,1320 item_id,1321 ) {1322 Some(amount)1323 } else {1324 fail!(Error::<T>::NoPermission);1325 };13261327 if collection.access == AccessMode::WhiteList {1328 Self::check_white_list(&collection, &sender)?;1329 Self::check_white_list(&collection, &spender)?;1330 }13311332 let allowance: u128 = amount1333 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1334 .ok_or(Error::<T>::NumOverflow)?;1335 if let Some(limit) = allowance_limit {1336 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1337 }1338 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);13391340 if matches!(collection.mode, CollectionMode::NFT) {1341 1342 collection.log(ERC721Events::Approval {1343 owner: *sender.as_eth(),1344 approved: *spender.as_eth(),1345 token_id: item_id.into(),1346 });1347 }13481349 if matches!(collection.mode, CollectionMode::Fungible(_)) {1350 1351 collection.log(ERC20Events::Approval {1352 owner: *sender.as_eth(),1353 spender: *spender.as_eth(),1354 value: allowance.into()1355 });1356 }13571358 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1359 Ok(())1360 }13611362 pub fn transfer_from_internal(1363 sender: &T::CrossAccountId,1364 from: &T::CrossAccountId,1365 recipient: &T::CrossAccountId,1366 collection: &CollectionHandle<T>,1367 item_id: TokenId,1368 amount: u128,1369 ) -> DispatchResult {1370 collection.consume_gas(2000000)?;1371 1372 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13731374 1375 Self::is_correct_transfer(&collection, &recipient)?;13761377 1378 ensure!(1379 approval >= amount || 1380 (1381 collection.limits.owner_can_transfer &&1382 Self::is_owner_or_admin_permissions(&collection, &sender)1383 ),1384 Error::<T>::NoPermission1385 );13861387 if collection.access == AccessMode::WhiteList {1388 Self::check_white_list(&collection, &sender)?;1389 Self::check_white_list(&collection, &recipient)?;1390 }13911392 1393 let allowance = approval.saturating_sub(amount);1394 if allowance > 0 {1395 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1396 } else {1397 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1398 }13991400 match collection.mode {1401 CollectionMode::NFT => {1402 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1403 }1404 CollectionMode::Fungible(_) => {1405 Self::transfer_fungible(&collection, amount, &from, &recipient)?1406 }1407 CollectionMode::ReFungible => {1408 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1409 }1410 _ => ()1411 };14121413 if matches!(collection.mode, CollectionMode::Fungible(_)) {1414 collection.log(ERC20Events::Approval {1415 owner: *from.as_eth(),1416 spender: *sender.as_eth(),1417 value: allowance.into()1418 });1419 }14201421 Ok(())1422 }14231424 pub fn set_variable_meta_data_internal(1425 sender: &T::CrossAccountId,1426 collection: &CollectionHandle<T>, 1427 item_id: TokenId,1428 data: Vec<u8>,1429 ) -> DispatchResult {1430 Self::token_exists(&collection, item_id)?;14311432 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);14331434 1435 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1436 Self::is_owner_or_admin_permissions(&collection, &sender),1437 Error::<T>::NoPermission);14381439 match collection.mode1440 {1441 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1442 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1443 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1444 _ => fail!(Error::<T>::UnexpectedCollectionType)1445 };14461447 Ok(())1448 }14491450 pub fn create_multiple_items_internal(1451 sender: &T::CrossAccountId,1452 collection: &CollectionHandle<T>,1453 owner: &T::CrossAccountId,1454 items_data: Vec<CreateItemData>,1455 ) -> DispatchResult {1456 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;14571458 for data in &items_data {1459 Self::validate_create_item_args(&collection, data)?;1460 }1461 for data in &items_data {1462 Self::create_item_no_validation(&collection, owner, data.clone())?;1463 }14641465 Ok(())1466 }14671468 pub fn burn_item_internal(1469 sender: &T::CrossAccountId,1470 collection: &CollectionHandle<T>,1471 item_id: TokenId,1472 value: u128,1473 ) -> DispatchResult {1474 ensure!(1475 Self::is_item_owner(&sender, &collection, item_id) ||1476 (1477 collection.limits.owner_can_transfer &&1478 Self::is_owner_or_admin_permissions(&collection, &sender)1479 ),1480 Error::<T>::NoPermission1481 );14821483 if collection.access == AccessMode::WhiteList {1484 Self::check_white_list(&collection, &sender)?;1485 }14861487 match collection.mode1488 {1489 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1490 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1491 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1492 _ => ()1493 };14941495 Ok(())1496 }14971498 pub fn toggle_white_list_internal(1499 sender: &T::CrossAccountId,1500 collection: &CollectionHandle<T>,1501 address: &T::CrossAccountId,1502 whitelisted: bool,1503 ) -> DispatchResult {1504 Self::check_owner_or_admin_permissions(&collection, &sender)?;15051506 if whitelisted {1507 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1508 } else {1509 <WhiteList<T>>::remove(collection.id, address.as_sub());1510 }15111512 Ok(())1513 }15141515 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1516 let collection_id = collection.id;15171518 1519 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1520 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1521 1522 Ok(())1523 }15241525 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1526 let collection_id = collection.id;15271528 1529 let total_items: u32 = ItemListIndex::get(collection_id)1530 .checked_add(amount)1531 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1532 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1533 .checked_add(amount)1534 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1535 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1536 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);15371538 if !Self::is_owner_or_admin_permissions(collection, &sender) {1539 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1540 Self::check_white_list(collection, owner)?;1541 Self::check_white_list(collection, sender)?;1542 }15431544 Ok(())1545 }15461547 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1548 match target_collection.mode1549 {1550 CollectionMode::NFT => {1551 if let CreateItemData::NFT(data) = data {1552 1553 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1554 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1555 } else {1556 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1557 }1558 },1559 CollectionMode::Fungible(_) => {1560 if let CreateItemData::Fungible(_) = data {1561 } else {1562 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1563 }1564 },1565 CollectionMode::ReFungible => {1566 if let CreateItemData::ReFungible(data) = data {15671568 1569 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1570 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15711572 1573 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1574 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1575 } else {1576 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1577 }1578 },1579 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1580 };15811582 Ok(())1583 }15841585 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1586 match data1587 {1588 CreateItemData::NFT(data) => {1589 let item = NftItemType {1590 owner: owner.clone(),1591 const_data: data.const_data,1592 variable_data: data.variable_data1593 };15941595 Self::add_nft_item(collection, item)?;1596 },1597 CreateItemData::Fungible(data) => {1598 Self::add_fungible_item(collection, &owner, data.value)?;1599 },1600 CreateItemData::ReFungible(data) => {1601 let mut owner_list = Vec::new();1602 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16031604 let item = ReFungibleItemType {1605 owner: owner_list,1606 const_data: data.const_data,1607 variable_data: data.variable_data1608 };16091610 Self::add_refungible_item(collection, item)?;1611 }1612 };16131614 Ok(())1615 }16161617 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1618 let collection_id = collection.id;16191620 1621 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16221623 1624 let item = FungibleItemType {1625 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1626 };1627 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16281629 1630 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1631 .checked_add(value)1632 .ok_or(Error::<T>::NumOverflow)?;1633 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16341635 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1636 Ok(())1637 }16381639 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1640 let collection_id = collection.id;16411642 let current_index = <ItemListIndex>::get(collection_id)1643 .checked_add(1)1644 .ok_or(Error::<T>::NumOverflow)?;1645 let itemcopy = item.clone();16461647 ensure!(1648 item.owner.len() == 1,1649 Error::<T>::BadCreateRefungibleCall,1650 );1651 let item_owner = item.owner.first().expect("only one owner is defined");16521653 let value = item_owner.fraction;1654 let owner = item_owner.owner.clone();16551656 Self::add_token_index(collection_id, current_index, &owner)?;16571658 <ItemListIndex>::insert(collection_id, current_index);1659 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16601661 1662 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1663 .checked_add(value)1664 .ok_or(Error::<T>::NumOverflow)?;1665 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16661667 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1668 Ok(())1669 }16701671 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1672 let collection_id = collection.id;16731674 let current_index = <ItemListIndex>::get(collection_id)1675 .checked_add(1)1676 .ok_or(Error::<T>::NumOverflow)?;16771678 let item_owner = item.owner.clone();1679 Self::add_token_index(collection_id, current_index, &item.owner)?;16801681 <ItemListIndex>::insert(collection_id, current_index);1682 <NftItemList<T>>::insert(collection_id, current_index, item);16831684 1685 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1686 .checked_add(1)1687 .ok_or(Error::<T>::NumOverflow)?;1688 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);16891690 collection.log(ERC721Events::Transfer {1691 from: H160::default(),1692 to: *item_owner.as_eth(),1693 token_id: current_index.into(),1694 });1695 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1696 Ok(())1697 }16981699 fn burn_refungible_item(1700 collection: &CollectionHandle<T>,1701 item_id: TokenId,1702 owner: &T::CrossAccountId,1703 ) -> DispatchResult {1704 let collection_id = collection.id;17051706 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1707 .ok_or(Error::<T>::TokenNotFound)?;1708 let rft_balance = token1709 .owner1710 .iter()1711 .find(|&i| i.owner == *owner)1712 .ok_or(Error::<T>::TokenNotFound)?;1713 Self::remove_token_index(collection_id, item_id, owner)?;17141715 1716 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1717 .checked_sub(rft_balance.fraction)1718 .ok_or(Error::<T>::NumOverflow)?;1719 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17201721 1722 let index = token1723 .owner1724 .iter()1725 .position(|i| i.owner == *owner)1726 .expect("owned item is exists");1727 token.owner.remove(index);1728 let owner_count = token.owner.len();17291730 1731 if owner_count == 0 {1732 <ReFungibleItemList<T>>::remove(collection_id, item_id);1733 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1734 }1735 else {1736 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1737 }17381739 Ok(())1740 }17411742 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1743 let collection_id = collection.id;17441745 let item = <NftItemList<T>>::get(collection_id, item_id)1746 .ok_or(Error::<T>::TokenNotFound)?;1747 Self::remove_token_index(collection_id, item_id, &item.owner)?;17481749 1750 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1751 .checked_sub(1)1752 .ok_or(Error::<T>::NumOverflow)?;1753 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1754 <NftItemList<T>>::remove(collection_id, item_id);1755 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17561757 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1758 Ok(())1759 }17601761 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1762 let collection_id = collection.id;17631764 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1765 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17661767 1768 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1769 .checked_sub(value)1770 .ok_or(Error::<T>::NumOverflow)?;1771 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17721773 if balance.value - value > 0 {1774 balance.value -= value;1775 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1776 }1777 else {1778 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1779 }17801781 collection.log(ERC20Events::Transfer {1782 from: *owner.as_eth(),1783 to: H160::default(),1784 value: value.into(),1785 });1786 Ok(())1787 }17881789 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1790 Ok(<CollectionHandle<T>>::get(collection_id)1791 .ok_or(Error::<T>::CollectionNotFound)?)1792 }17931794 fn save_collection(collection: CollectionHandle<T>) {1795 <CollectionById<T>>::insert(collection.id, collection.into_inner());1796 }17971798 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1799 if collection.logs.is_empty() {1800 return Ok(())1801 }1802 T::EthereumTransactionSender::submit_logs_transaction(1803 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1804 collection.logs.retrieve_logs(),1805 )1806 }18071808 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1809 ensure!(1810 *subject == target_collection.owner,1811 Error::<T>::NoPermission1812 );18131814 Ok(())1815 }18161817 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1818 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1819 }18201821 fn check_owner_or_admin_permissions(1822 collection: &CollectionHandle<T>,1823 subject: &T::CrossAccountId,1824 ) -> DispatchResult {1825 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18261827 Ok(())1828 }18291830 fn owned_amount(1831 subject: &T::CrossAccountId,1832 target_collection: &CollectionHandle<T>,1833 item_id: TokenId,1834 ) -> Option<u128> {1835 let collection_id = target_collection.id;18361837 match target_collection.mode {1838 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)1839 .then(|| 1),1840 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())1841 .value),1842 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1843 .owner1844 .iter()1845 .find(|i| i.owner == *subject)1846 .map(|i| i.fraction),1847 CollectionMode::Invalid => None,1848 }1849 }18501851 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1852 match target_collection.mode {1853 CollectionMode::Fungible(_) => true,1854 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),1855 }1856 }18571858 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {1859 let collection_id = collection.id;18601861 let mes = Error::<T>::AddresNotInWhiteList;1862 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);18631864 Ok(())1865 }18661867 1868 1869 fn token_exists(1870 target_collection: &CollectionHandle<T>,1871 item_id: TokenId,1872 ) -> DispatchResult {1873 let collection_id = target_collection.id;1874 let exists = match target_collection.mode1875 {1876 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1877 CollectionMode::Fungible(_) => true,1878 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1879 _ => false1880 };18811882 ensure!(exists == true, Error::<T>::TokenNotFound);1883 Ok(())1884 }18851886 fn transfer_fungible(1887 collection: &CollectionHandle<T>,1888 value: u128,1889 owner: &T::CrossAccountId,1890 recipient: &T::CrossAccountId,1891 ) -> DispatchResult {1892 let collection_id = collection.id;18931894 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1895 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18961897 1898 Self::add_fungible_item(collection, recipient, value)?;18991900 1901 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19021903 1904 if balance.value == value {1905 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1906 }1907 else {1908 balance.value -= value;1909 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1910 }19111912 collection.log(ERC20Events::Transfer {1913 from: *owner.as_eth(),1914 to: *recipient.as_eth(),1915 value: value.into(),1916 });1917 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));19181919 Ok(())1920 }19211922 fn transfer_refungible(1923 collection: &CollectionHandle<T>,1924 item_id: TokenId,1925 value: u128,1926 owner: T::CrossAccountId,1927 new_owner: T::CrossAccountId,1928 ) -> DispatchResult {1929 let collection_id = collection.id;1930 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1931 .ok_or(Error::<T>::TokenNotFound)?;19321933 let item = full_item1934 .owner1935 .iter()1936 .filter(|i| i.owner == owner)1937 .next()1938 .ok_or(Error::<T>::TokenNotFound)?;1939 let amount = item.fraction;19401941 ensure!(amount >= value, Error::<T>::TokenValueTooLow);19421943 1944 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())1945 .checked_sub(value)1946 .ok_or(Error::<T>::NumOverflow)?;1947 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);19481949 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())1950 .checked_add(value)1951 .ok_or(Error::<T>::NumOverflow)?;1952 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);19531954 let old_owner = item.owner.clone();1955 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19561957 1958 if amount == value && !new_owner_has_account {1959 1960 1961 let mut new_full_item = full_item.clone();1962 new_full_item1963 .owner1964 .iter_mut()1965 .find(|i| i.owner == owner)1966 .expect("old owner does present in refungible")1967 .owner = new_owner.clone();1968 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19691970 1971 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1972 } else {1973 let mut new_full_item = full_item.clone();1974 new_full_item1975 .owner1976 .iter_mut()1977 .find(|i| i.owner == owner)1978 .expect("old owner does present in refungible")1979 .fraction -= value;19801981 1982 if new_owner_has_account {1983 1984 new_full_item1985 .owner1986 .iter_mut()1987 .find(|i| i.owner == new_owner)1988 .expect("new owner has account")1989 .fraction += value;1990 } else {1991 1992 new_full_item.owner.push(Ownership {1993 owner: new_owner.clone(),1994 fraction: value,1995 });1996 Self::add_token_index(collection_id, item_id, &new_owner)?;1997 }19981999 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2000 }20012002 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));20032004 Ok(())2005 }20062007 fn transfer_nft(2008 collection: &CollectionHandle<T>,2009 item_id: TokenId,2010 sender: T::CrossAccountId,2011 new_owner: T::CrossAccountId,2012 ) -> DispatchResult {2013 let collection_id = collection.id;2014 let mut item = <NftItemList<T>>::get(collection_id, item_id)2015 .ok_or(Error::<T>::TokenNotFound)?;20162017 ensure!(2018 sender == item.owner,2019 Error::<T>::MustBeTokenOwner2020 );20212022 2023 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2024 .checked_sub(1)2025 .ok_or(Error::<T>::NumOverflow)?;2026 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20272028 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2029 .checked_add(1)2030 .ok_or(Error::<T>::NumOverflow)?;2031 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20322033 2034 let old_owner = item.owner.clone();2035 item.owner = new_owner.clone();2036 <NftItemList<T>>::insert(collection_id, item_id, item);20372038 2039 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20402041 collection.log(ERC721Events::Transfer {2042 from: *sender.as_eth(),2043 to: *new_owner.as_eth(),2044 token_id: item_id.into(),2045 });2046 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));20472048 Ok(())2049 }2050 2051 fn set_re_fungible_variable_data(2052 collection: &CollectionHandle<T>,2053 item_id: TokenId,2054 data: Vec<u8>2055 ) -> DispatchResult {2056 let collection_id = collection.id;2057 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2058 .ok_or(Error::<T>::TokenNotFound)?;20592060 item.variable_data = data;20612062 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20632064 Ok(())2065 }20662067 fn set_nft_variable_data(2068 collection: &CollectionHandle<T>,2069 item_id: TokenId,2070 data: Vec<u8>2071 ) -> DispatchResult {2072 let collection_id = collection.id;2073 let mut item = <NftItemList<T>>::get(collection_id, item_id)2074 .ok_or(Error::<T>::TokenNotFound)?;2075 2076 item.variable_data = data;20772078 <NftItemList<T>>::insert(collection_id, item_id, item);2079 2080 Ok(())2081 }20822083 #[allow(dead_code)]2084 fn init_collection(item: &Collection<T>) {2085 2086 assert!(2087 item.decimal_points <= MAX_DECIMAL_POINTS,2088 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2089 );2090 assert!(2091 item.name.len() <= 64,2092 "Collection name can not be longer than 63 char"2093 );2094 assert!(2095 item.name.len() <= 256,2096 "Collection description can not be longer than 255 char"2097 );2098 assert!(2099 item.token_prefix.len() <= 16,2100 "Token prefix can not be longer than 15 char"2101 );21022103 2104 let next_id = CreatedCollectionCount::get()2105 .checked_add(1)2106 .unwrap();21072108 CreatedCollectionCount::put(next_id);2109 }21102111 #[allow(dead_code)]2112 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2113 let current_index = <ItemListIndex>::get(collection_id)2114 .checked_add(1)2115 .unwrap();21162117 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21182119 <ItemListIndex>::insert(collection_id, current_index);21202121 2122 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2123 .checked_add(1)2124 .unwrap();2125 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2126 }21272128 #[allow(dead_code)]2129 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2130 let current_index = <ItemListIndex>::get(collection_id)2131 .checked_add(1)2132 .unwrap();21332134 Self::add_token_index(collection_id, current_index, owner).unwrap();21352136 <ItemListIndex>::insert(collection_id, current_index);21372138 2139 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2140 .checked_add(item.value)2141 .unwrap();2142 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2143 }21442145 #[allow(dead_code)]2146 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2147 let current_index = <ItemListIndex>::get(collection_id)2148 .checked_add(1)2149 .unwrap();21502151 let value = item.owner.first().unwrap().fraction;2152 let owner = item.owner.first().unwrap().owner.clone();21532154 Self::add_token_index(collection_id, current_index, &owner).unwrap();21552156 <ItemListIndex>::insert(collection_id, current_index);21572158 2159 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2160 .checked_add(value)2161 .unwrap();2162 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2163 }21642165 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2166 2167 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {21682169 2170 let count = <AccountItemCount<T>>::get(owner.as_sub());2171 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21722173 <AccountItemCount<T>>::insert(owner.as_sub(), count2174 .checked_add(1)2175 .ok_or(Error::<T>::NumOverflow)?);2176 }2177 else {2178 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2179 }21802181 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2182 if list_exists {2183 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2184 let item_contains = list.contains(&item_index.clone());21852186 if !item_contains {2187 list.push(item_index.clone());2188 }21892190 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2191 } else {2192 let mut itm = Vec::new();2193 itm.push(item_index.clone());2194 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2195 }21962197 Ok(())2198 }21992200 fn remove_token_index(2201 collection_id: CollectionId,2202 item_index: TokenId,2203 owner: &T::CrossAccountId,2204 ) -> DispatchResult {22052206 2207 <AccountItemCount<T>>::insert(owner.as_sub(), 2208 <AccountItemCount<T>>::get(owner.as_sub())2209 .checked_sub(1)2210 .ok_or(Error::<T>::NumOverflow)?);221122122213 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2214 if list_exists {2215 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2216 let item_contains = list.contains(&item_index.clone());22172218 if item_contains {2219 list.retain(|&item| item != item_index);2220 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2221 }2222 }22232224 Ok(())2225 }22262227 fn move_token_index(2228 collection_id: CollectionId,2229 item_index: TokenId,2230 old_owner: &T::CrossAccountId,2231 new_owner: &T::CrossAccountId,2232 ) -> DispatchResult {2233 Self::remove_token_index(collection_id, item_index, old_owner)?;2234 Self::add_token_index(collection_id, item_index, new_owner)?;22352236 Ok(())2237 }2238 2239 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2240 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22412242 Ok(())2243 }2244}22452246sp_api::decl_runtime_apis! {2247 pub trait NftApi {2248 2249 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2250 }2251}