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 nft_data_structs::{36 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,37 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,38 CollectionId, CollectionMode, TokenId, 39 SchemaVersion, SponsorshipState, Ownership,40 NftItemType, FungibleItemType, ReFungibleItemType41};4243#[cfg(test)]44mod mock;4546#[cfg(test)]47mod tests;4849mod default_weights;50mod eth;5152pub use eth::NftErcSupport;53pub use eth::account::*;54use eth::erc::{ERC20Events, ERC721Events};5556#[cfg(feature = "runtime-benchmarks")]57mod benchmarking;5859pub trait WeightInfo {60 fn create_collection() -> Weight;61 fn destroy_collection() -> Weight;62 fn add_to_white_list() -> Weight;63 fn remove_from_white_list() -> Weight;64 fn set_public_access_mode() -> Weight;65 fn set_mint_permission() -> Weight;66 fn change_collection_owner() -> Weight;67 fn add_collection_admin() -> Weight;68 fn remove_collection_admin() -> Weight;69 fn set_collection_sponsor() -> Weight;70 fn confirm_sponsorship() -> Weight;71 fn remove_collection_sponsor() -> Weight;72 fn create_item(s: usize) -> Weight;73 fn burn_item() -> Weight;74 fn transfer() -> Weight;75 fn approve() -> Weight;76 fn transfer_from() -> Weight;77 fn set_offchain_schema() -> Weight;78 fn set_const_on_chain_schema() -> Weight;79 fn set_variable_on_chain_schema() -> Weight;80 fn set_variable_meta_data() -> Weight;81 fn enable_contract_sponsoring() -> Weight;82 fn set_schema_version() -> Weight;83 fn set_chain_limits() -> Weight;84 fn set_contract_sponsoring_rate_limit() -> Weight;85 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;86 fn toggle_contract_white_list() -> Weight;87 fn add_to_contract_white_list() -> Weight;88 fn remove_from_contract_white_list() -> Weight;89 fn set_collection_limits() -> Weight;90}9192decl_error! {93 94 pub enum Error for Module<T: Config> {95 96 TotalCollectionsLimitExceeded,97 98 CollectionDecimalPointLimitExceeded, 99 100 CollectionNameLimitExceeded, 101 102 CollectionDescriptionLimitExceeded, 103 104 CollectionTokenPrefixLimitExceeded,105 106 CollectionNotFound,107 108 TokenNotFound,109 110 AdminNotFound,111 112 NumOverflow, 113 114 AlreadyAdmin, 115 116 NoPermission,117 118 ConfirmUnsetSponsorFail,119 120 PublicMintingNotAllowed,121 122 MustBeTokenOwner,123 124 TokenValueTooLow,125 126 NftSizeLimitExceeded,127 128 ApproveNotFound,129 130 TokenValueNotEnough,131 132 ApproveRequired,133 134 AddresNotInWhiteList,135 136 CollectionAdminsLimitExceeded,137 138 AddressOwnershipLimitExceeded,139 140 EmptyArgument,141 142 TokenConstDataLimitExceeded,143 144 TokenVariableDataLimitExceeded,145 146 NotNftDataUsedToMintNftCollectionToken,147 148 NotFungibleDataUsedToMintFungibleCollectionToken,149 150 NotReFungibleDataUsedToMintReFungibleCollectionToken,151 152 UnexpectedCollectionType,153 154 CantStoreMetadataInFungibleTokens,155 156 CollectionTokenLimitExceeded,157 158 AccountTokenLimitExceeded,159 160 CollectionLimitBoundsExceeded,161 162 OwnerPermissionsCantBeReverted,163 164 SchemaDataLimitExceeded,165 166 WrongRefungiblePieces,167 168 BadCreateRefungibleCall,169 170 OutOfGas,171 }172}173174pub struct CollectionHandle<T: system::Config> {175 pub id: CollectionId,176 pub collection: Collection<T>,177}178179impl<T: frame_system::Config> Deref for CollectionHandle<T> {180 type Target = Collection<T>;181182 fn deref(&self) -> &Self::Target {183 &self.collection184 }185}186187impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {188 fn deref_mut(&mut self) -> &mut Self::Target {189 &mut self.collection190 }191}192193pub trait Config: system::Config + Sized {194 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;195196 197 type WeightInfo: WeightInfo;198199 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;200 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;201 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;202203 type CrossAccountId: CrossAccountId<Self::AccountId>;204 type Currency: Currency<Self::AccountId>;205 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;206 type TreasuryAccountId: Get<Self::AccountId>;207208 type EthereumChainId: Get<u64>;209 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;210}211212213214215216217218219220221222223224225226227228229230231232233234decl_storage! {235 trait Store for Module<T: Config> as Nft {236237 238 239 CreatedCollectionCount: u32;240 241 ChainVersion: u64;242 243 244 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;245 246247 248 pub ChainLimit get(fn chain_limit) config(): ChainLimits;249 250251 252 253 254 DestroyedCollectionCount: u32;255 256 257 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;258 259260 261 262 263 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;264 265 266 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;267 268 269 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;270 271272 273 274 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;275276 277 278 279 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;280281 282 283 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;284 285 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;286 287 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;288 289290 291 292 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;293 294295 296 297 298 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;299 300 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;301 302 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;303 304 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;305 306307 308 309 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;310 311 312 313 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;314 315 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;316 317 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;318 319 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;320 321 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 322 323 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 324 325 }326 add_extra_genesis {327 build(|config: &GenesisConfig<T>| {328 329 for (_num, _c) in &config.collection_id {330 <Module<T>>::init_collection(_c);331 }332333 for (_num, _c, _i) in &config.nft_item_id {334 <Module<T>>::init_nft_token(*_c, _i);335 }336337 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {338 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);339 }340341 for (_num, _c, _i) in &config.refungible_item_id {342 <Module<T>>::init_refungible_token(*_c, _i);343 }344 })345 }346}347348decl_event!(349 pub enum Event<T>350 where351 AccountId = <T as frame_system::Config>::AccountId,352 CrossAccountId = <T as Config>::CrossAccountId,353 {354 355 356 357 358 359 360 361 362 363 CollectionCreated(CollectionId, u8, AccountId),364365 366 367 368 369 370 371 372 373 374 ItemCreated(CollectionId, TokenId, CrossAccountId),375376 377 378 379 380 381 382 383 ItemDestroyed(CollectionId, TokenId),384385 386 387 388 389 390 391 392 393 394 395 396 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),397398 399 400 401 402 403 404 405 406 407 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),408 }409);410411decl_module! {412 pub struct Module<T: Config> for enum Call 413 where 414 origin: T::Origin415 {416 fn deposit_event() = default;417 type Error = Error<T>;418419 fn on_initialize(_now: T::BlockNumber) -> Weight {420 0421 }422423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 #[weight = <T as Config>::WeightInfo::create_collection()]440 #[transactional]441 pub fn create_collection(origin,442 collection_name: Vec<u16>,443 collection_description: Vec<u16>,444 token_prefix: Vec<u8>,445 mode: CollectionMode) -> DispatchResult {446447 448 let who = ensure_signed(origin)?;449450 451 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();452 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(453 &T::TreasuryAccountId::get(),454 T::CollectionCreationPrice::get(),455 ));456 <T as Config>::Currency::settle(457 &who,458 imbalance,459 WithdrawReasons::TRANSFER,460 ExistenceRequirement::KeepAlive,461 ).map_err(|_| Error::<T>::NoPermission)?;462463 let decimal_points = match mode {464 CollectionMode::Fungible(points) => points,465 _ => 0466 };467468 let chain_limit = ChainLimit::get();469470 let created_count = CreatedCollectionCount::get();471 let destroyed_count = DestroyedCollectionCount::get();472473 474 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);475476 477 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);478 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);479 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);480 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);481482 483 let next_id = created_count484 .checked_add(1)485 .ok_or(Error::<T>::NumOverflow)?;486487 CreatedCollectionCount::put(next_id);488489 let limits = CollectionLimits {490 sponsored_data_size: chain_limit.custom_data_limit,491 ..Default::default()492 };493494 495 let new_collection = Collection {496 owner: who.clone(),497 name: collection_name,498 mode: mode.clone(),499 mint_mode: false,500 access: AccessMode::Normal,501 description: collection_description,502 decimal_points: decimal_points,503 token_prefix: token_prefix,504 offchain_schema: Vec::new(),505 schema_version: SchemaVersion::ImageURL,506 sponsorship: SponsorshipState::Disabled,507 variable_on_chain_schema: Vec::new(),508 const_on_chain_schema: Vec::new(),509 limits,510 };511512 513 <CollectionById<T>>::insert(next_id, new_collection);514515 516 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));517518 Ok(())519 }520521 522 523 524 525 526 527 528 529 530 #[weight = <T as Config>::WeightInfo::destroy_collection()]531 #[transactional]532 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {533534 let sender = ensure_signed(origin)?;535 let collection = Self::get_collection(collection_id)?;536 Self::check_owner_permissions(&collection, &sender)?;537 if !collection.limits.owner_can_destroy {538 fail!(Error::<T>::NoPermission);539 }540541 <AddressTokens<T>>::remove_prefix(collection_id);542 <Allowances<T>>::remove_prefix(collection_id);543 <Balance<T>>::remove_prefix(collection_id);544 <ItemListIndex>::remove(collection_id);545 <AdminList<T>>::remove(collection_id);546 <CollectionById<T>>::remove(collection_id);547 <WhiteList<T>>::remove_prefix(collection_id);548549 <NftItemList<T>>::remove_prefix(collection_id);550 <FungibleItemList<T>>::remove_prefix(collection_id);551 <ReFungibleItemList<T>>::remove_prefix(collection_id);552553 <NftTransferBasket<T>>::remove_prefix(collection_id);554 <FungibleTransferBasket<T>>::remove_prefix(collection_id);555 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);556557 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);558559 DestroyedCollectionCount::put(DestroyedCollectionCount::get()560 .checked_add(1)561 .ok_or(Error::<T>::NumOverflow)?);562563 Ok(())564 }565566 567 568 569 570 571 572 573 574 575 576 577 578 #[weight = <T as Config>::WeightInfo::add_to_white_list()]579 #[transactional]580 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{581582 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);583 let collection = Self::get_collection(collection_id)?;584585 Self::toggle_white_list_internal(586 &sender,587 &collection,588 &address,589 true,590 )?;591592 Ok(())593 }594595 596 597 598 599 600 601 602 603 604 605 606 607 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]608 #[transactional]609 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{610611 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);612 let collection = Self::get_collection(collection_id)?;613614 Self::toggle_white_list_internal(615 &sender,616 &collection,617 &address,618 false,619 )?;620621 Ok(())622 }623624 625 626 627 628 629 630 631 632 633 634 635 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]636 #[transactional]637 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult638 {639 let sender = ensure_signed(origin)?;640641 let mut target_collection = Self::get_collection(collection_id)?;642 Self::check_owner_permissions(&target_collection, &sender)?;643 target_collection.access = mode;644 Self::save_collection(target_collection);645646 Ok(())647 }648649 650 651 652 653 654 655 656 657 658 659 660 661 662 #[weight = <T as Config>::WeightInfo::set_mint_permission()]663 #[transactional]664 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult665 {666 let sender = ensure_signed(origin)?;667668 let mut target_collection = Self::get_collection(collection_id)?;669 Self::check_owner_permissions(&target_collection, &sender)?;670 target_collection.mint_mode = mint_permission;671 Self::save_collection(target_collection);672673 Ok(())674 }675676 677 678 679 680 681 682 683 684 685 686 687 #[weight = <T as Config>::WeightInfo::change_collection_owner()]688 #[transactional]689 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {690691 let sender = ensure_signed(origin)?;692 let mut target_collection = Self::get_collection(collection_id)?;693 Self::check_owner_permissions(&target_collection, &sender)?;694 target_collection.owner = new_owner;695 Self::save_collection(target_collection);696697 Ok(())698 }699700 701 702 703 704 705 706 707 708 709 710 711 712 713 #[weight = <T as Config>::WeightInfo::add_collection_admin()]714 #[transactional]715 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {716 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);717 let collection = Self::get_collection(collection_id)?;718 Self::check_owner_or_admin_permissions(&collection, &sender)?;719 let mut admin_arr = <AdminList<T>>::get(collection_id);720721 match admin_arr.binary_search(&new_admin_id) {722 Ok(_) => {},723 Err(idx) => {724 let limits = ChainLimit::get();725 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);726 admin_arr.insert(idx, new_admin_id);727 <AdminList<T>>::insert(collection_id, admin_arr);728 }729 }730 Ok(())731 }732733 734 735 736 737 738 739 740 741 742 743 744 745 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]746 #[transactional]747 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {748 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);749 let collection = Self::get_collection(collection_id)?;750 Self::check_owner_or_admin_permissions(&collection, &sender)?;751 let mut admin_arr = <AdminList<T>>::get(collection_id);752753 match admin_arr.binary_search(&account_id) {754 Ok(idx) => {755 admin_arr.remove(idx);756 <AdminList<T>>::insert(collection_id, admin_arr);757 },758 Err(_) => {}759 }760 Ok(())761 }762763 764 765 766 767 768 769 770 771 772 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]773 #[transactional]774 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {775 let sender = ensure_signed(origin)?;776 let mut target_collection = Self::get_collection(collection_id)?;777 Self::check_owner_permissions(&target_collection, &sender)?;778779 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);780 Self::save_collection(target_collection);781782 Ok(())783 }784785 786 787 788 789 790 791 792 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]793 #[transactional]794 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {795 let sender = ensure_signed(origin)?;796797 let mut target_collection = Self::get_collection(collection_id)?;798 ensure!(799 target_collection.sponsorship.pending_sponsor() == Some(&sender),800 Error::<T>::ConfirmUnsetSponsorFail801 );802803 target_collection.sponsorship = SponsorshipState::Confirmed(sender);804 Self::save_collection(target_collection);805806 Ok(())807 }808809 810 811 812 813 814 815 816 817 818 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]819 #[transactional]820 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {821 let sender = ensure_signed(origin)?;822823 let mut target_collection = Self::get_collection(collection_id)?;824 Self::check_owner_permissions(&target_collection, &sender)?;825826 target_collection.sponsorship = SponsorshipState::Disabled;827 Self::save_collection(target_collection);828829 Ok(())830 }831832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855856 #[weight = <T as Config>::WeightInfo::create_item(data.len())]857 #[transactional]858 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {859 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);860 let collection = Self::get_collection(collection_id)?;861862 Self::create_item_internal(&sender, &collection, &owner, data)?;863864 Self::submit_logs(collection)?;865 Ok(())866 }867868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()887 .map(|data| { data.len() })888 .sum())]889 #[transactional]890 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {891892 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);893 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894 let collection = Self::get_collection(collection_id)?;895896 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;897898 Self::submit_logs(collection)?;899 Ok(())900 }901902 903 904 905 906 907 908 909 910 911 912 913 914 915 #[weight = <T as Config>::WeightInfo::burn_item()]916 #[transactional]917 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {918919 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);920 let target_collection = Self::get_collection(collection_id)?;921922 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;923924 Self::submit_logs(target_collection)?;925 Ok(())926 }927928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 #[weight = <T as Config>::WeightInfo::transfer()]952 #[transactional]953 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {954 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);955 let collection = Self::get_collection(collection_id)?;956957 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;958959 Self::submit_logs(collection)?;960 Ok(())961 }962963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 #[weight = <T as Config>::WeightInfo::approve()]979 #[transactional]980 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {981 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);982 let collection = Self::get_collection(collection_id)?;983984 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;985986 Self::submit_logs(collection)?;987 Ok(())988 }989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 #[weight = <T as Config>::WeightInfo::transfer_from()]1010 #[transactional]1011 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1012 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013 let collection = Self::get_collection(collection_id)?;10141015 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10161017 Self::submit_logs(collection)?;1018 Ok(())1019 }1020 1021 1022 1023 1024 10251026 10271028 10291030 1031 10321033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1046 #[transactional]1047 pub fn set_variable_meta_data (1048 origin,1049 collection_id: CollectionId,1050 item_id: TokenId,1051 data: Vec<u8>1052 ) -> DispatchResult {1053 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1054 1055 let collection = Self::get_collection(collection_id)?;10561057 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10581059 Ok(())1060 }1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 #[weight = <T as Config>::WeightInfo::set_schema_version()]1077 #[transactional]1078 pub fn set_schema_version(1079 origin,1080 collection_id: CollectionId,1081 version: SchemaVersion1082 ) -> DispatchResult {1083 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1084 let mut target_collection = Self::get_collection(collection_id)?;1085 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1086 target_collection.schema_version = version;1087 Self::save_collection(target_collection);10881089 Ok(())1090 }10911092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1105 #[transactional]1106 pub fn set_offchain_schema(1107 origin,1108 collection_id: CollectionId,1109 schema: Vec<u8>1110 ) -> DispatchResult {1111 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1112 let mut target_collection = Self::get_collection(collection_id)?;1113 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11141115 1116 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11171118 target_collection.offchain_schema = schema;1119 Self::save_collection(target_collection);11201121 Ok(())1122 }11231124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1137 #[transactional]1138 pub fn set_const_on_chain_schema (1139 origin,1140 collection_id: CollectionId,1141 schema: Vec<u8>1142 ) -> DispatchResult {1143 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1144 let mut target_collection = Self::get_collection(collection_id)?;1145 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11461147 1148 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11491150 target_collection.const_on_chain_schema = schema;1151 Self::save_collection(target_collection);11521153 Ok(())1154 }11551156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1169 #[transactional]1170 pub fn set_variable_on_chain_schema (1171 origin,1172 collection_id: CollectionId,1173 schema: Vec<u8>1174 ) -> DispatchResult {1175 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1176 let mut target_collection = Self::get_collection(collection_id)?;1177 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11781179 1180 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");11811182 target_collection.variable_on_chain_schema = schema;1183 Self::save_collection(target_collection);11841185 Ok(())1186 }11871188 1189 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1190 #[transactional]1191 pub fn set_chain_limits(1192 origin,1193 limits: ChainLimits1194 ) -> DispatchResult {11951196 #[cfg(not(feature = "runtime-benchmarks"))]1197 ensure_root(origin)?;11981199 <ChainLimit>::put(limits);1200 Ok(())1201 }12021203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1215 #[transactional]1216 pub fn enable_contract_sponsoring(1217 origin,1218 contract_address: T::AccountId,1219 enable: bool1220 ) -> DispatchResult {12211222 let sender = ensure_signed(origin)?;12231224 #[cfg(feature = "runtime-benchmarks")]1225 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12261227 Self::ensure_contract_owned(sender, &contract_address)?;12281229 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1230 Ok(())1231 }12321233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1251 #[transactional]1252 pub fn set_contract_sponsoring_rate_limit(1253 origin,1254 contract_address: T::AccountId,1255 rate_limit: T::BlockNumber1256 ) -> DispatchResult {1257 let sender = ensure_signed(origin)?;12581259 #[cfg(feature = "runtime-benchmarks")]1260 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12611262 Self::ensure_contract_owned(sender, &contract_address)?;1263 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1264 Ok(())1265 }12661267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1279 #[transactional]1280 pub fn toggle_contract_white_list(1281 origin,1282 contract_address: T::AccountId,1283 enable: bool1284 ) -> DispatchResult {1285 let sender = ensure_signed(origin)?;12861287 #[cfg(feature = "runtime-benchmarks")]1288 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12891290 Self::ensure_contract_owned(sender, &contract_address)?;1291 if enable {1292 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1293 } else {1294 <ContractWhiteListEnabled<T>>::remove(contract_address);1295 }1296 Ok(())1297 }1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1311 #[transactional]1312 pub fn add_to_contract_white_list(1313 origin,1314 contract_address: T::AccountId,1315 account_address: T::AccountId1316 ) -> DispatchResult {1317 let sender = ensure_signed(origin)?;13181319 #[cfg(feature = "runtime-benchmarks")]1320 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1321 1322 Self::ensure_contract_owned(sender, &contract_address)?; 1323 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1324 Ok(())1325 }13261327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1339 #[transactional]1340 pub fn remove_from_contract_white_list(1341 origin,1342 contract_address: T::AccountId,1343 account_address: T::AccountId1344 ) -> DispatchResult {1345 let sender = ensure_signed(origin)?;13461347 #[cfg(feature = "runtime-benchmarks")]1348 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13491350 Self::ensure_contract_owned(sender, &contract_address)?;1351 <ContractWhiteList<T>>::remove(contract_address, account_address);1352 Ok(())1353 }13541355 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1356 #[transactional]1357 pub fn set_collection_limits(1358 origin,1359 collection_id: u32,1360 new_limits: CollectionLimits<T::BlockNumber>,1361 ) -> DispatchResult {1362 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1363 let mut target_collection = Self::get_collection(collection_id)?;1364 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1365 let old_limits = &target_collection.limits;1366 let chain_limits = ChainLimit::get();13671368 1369 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1370 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1371 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1372 Error::<T>::CollectionLimitBoundsExceeded);13731374 1375 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1376 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);13771378 ensure!(1379 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1380 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1381 Error::<T>::OwnerPermissionsCantBeReverted,1382 );13831384 target_collection.limits = new_limits;1385 Self::save_collection(target_collection);13861387 Ok(())1388 } 1389 }1390}13911392impl<T: Config> Module<T> {1393 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1394 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1395 Self::validate_create_item_args(&collection, &data)?;1396 Self::create_item_no_validation(&collection, owner, data)?;13971398 Ok(())1399 }14001401 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1402 target_collection.consume_gas(2000000)?;1403 1404 Self::is_correct_transfer(target_collection, &recipient)?;14051406 1407 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1408 Self::is_owner_or_admin_permissions(target_collection, &sender),1409 Error::<T>::NoPermission);14101411 if target_collection.access == AccessMode::WhiteList {1412 Self::check_white_list(target_collection, &sender)?;1413 Self::check_white_list(target_collection, &recipient)?;1414 }14151416 match target_collection.mode1417 {1418 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1419 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1420 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1421 _ => ()1422 };14231424 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));14251426 Ok(())1427 }14281429 pub fn approve_internal(1430 sender: &T::CrossAccountId,1431 spender: &T::CrossAccountId,1432 collection: &CollectionHandle<T>,1433 item_id: TokenId,1434 amount: u1281435 ) -> DispatchResult {1436 collection.consume_gas(2000000)?;1437 Self::token_exists(&collection, item_id)?;14381439 1440 let bypasses_limits = collection.limits.owner_can_transfer &&1441 Self::is_owner_or_admin_permissions(1442 &collection,1443 &sender,1444 );14451446 let allowance_limit = if bypasses_limits {1447 None1448 } else if let Some(amount) = Self::owned_amount(1449 &sender,1450 &collection,1451 item_id,1452 ) {1453 Some(amount)1454 } else {1455 fail!(Error::<T>::NoPermission);1456 };14571458 if collection.access == AccessMode::WhiteList {1459 Self::check_white_list(&collection, &sender)?;1460 Self::check_white_list(&collection, &spender)?;1461 }14621463 let allowance: u128 = amount1464 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1465 .ok_or(Error::<T>::NumOverflow)?;1466 if let Some(limit) = allowance_limit {1467 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1468 }1469 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);14701471 if matches!(collection.mode, CollectionMode::NFT) {1472 1473 collection.log(ERC721Events::Approval {1474 owner: *sender.as_eth(),1475 approved: *spender.as_eth(),1476 token_id: item_id.into(),1477 });1478 }14791480 if matches!(collection.mode, CollectionMode::Fungible(_)) {1481 1482 collection.log(ERC20Events::Approval {1483 owner: *sender.as_eth(),1484 spender: *spender.as_eth(),1485 value: allowance.into()1486 });1487 }14881489 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1490 Ok(())1491 }14921493 pub fn transfer_from_internal(1494 sender: &T::CrossAccountId,1495 from: &T::CrossAccountId,1496 recipient: &T::CrossAccountId,1497 collection: &CollectionHandle<T>,1498 item_id: TokenId,1499 amount: u128,1500 ) -> DispatchResult {1501 collection.consume_gas(2000000)?;1502 1503 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));15041505 1506 Self::is_correct_transfer(&collection, &recipient)?;15071508 1509 ensure!(1510 approval >= amount || 1511 (1512 collection.limits.owner_can_transfer &&1513 Self::is_owner_or_admin_permissions(&collection, &sender)1514 ),1515 Error::<T>::NoPermission1516 );15171518 if collection.access == AccessMode::WhiteList {1519 Self::check_white_list(&collection, &sender)?;1520 Self::check_white_list(&collection, &recipient)?;1521 }15221523 1524 let allowance = approval.saturating_sub(amount);1525 if allowance > 0 {1526 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1527 } else {1528 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1529 }15301531 match collection.mode {1532 CollectionMode::NFT => {1533 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1534 }1535 CollectionMode::Fungible(_) => {1536 Self::transfer_fungible(&collection, amount, &from, &recipient)?1537 }1538 CollectionMode::ReFungible => {1539 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1540 }1541 _ => ()1542 };15431544 if matches!(collection.mode, CollectionMode::Fungible(_)) {1545 collection.log(ERC20Events::Approval {1546 owner: *from.as_eth(),1547 spender: *sender.as_eth(),1548 value: allowance.into()1549 });1550 }15511552 Ok(())1553 }15541555 pub fn set_variable_meta_data_internal(1556 sender: &T::CrossAccountId,1557 collection: &CollectionHandle<T>, 1558 item_id: TokenId,1559 data: Vec<u8>,1560 ) -> DispatchResult {1561 Self::token_exists(&collection, item_id)?;15621563 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15641565 1566 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1567 Self::is_owner_or_admin_permissions(&collection, &sender),1568 Error::<T>::NoPermission);15691570 match collection.mode1571 {1572 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1573 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1574 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1575 _ => fail!(Error::<T>::UnexpectedCollectionType)1576 };15771578 Ok(())1579 }15801581 pub fn create_multiple_items_internal(1582 sender: &T::CrossAccountId,1583 collection: &CollectionHandle<T>,1584 owner: &T::CrossAccountId,1585 items_data: Vec<CreateItemData>,1586 ) -> DispatchResult {1587 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;15881589 for data in &items_data {1590 Self::validate_create_item_args(&collection, data)?;1591 }1592 for data in &items_data {1593 Self::create_item_no_validation(&collection, owner, data.clone())?;1594 }15951596 Ok(())1597 }15981599 pub fn burn_item_internal(1600 sender: &T::CrossAccountId,1601 collection: &CollectionHandle<T>,1602 item_id: TokenId,1603 value: u128,1604 ) -> DispatchResult {1605 ensure!(1606 Self::is_item_owner(&sender, &collection, item_id) ||1607 (1608 collection.limits.owner_can_transfer &&1609 Self::is_owner_or_admin_permissions(&collection, &sender)1610 ),1611 Error::<T>::NoPermission1612 );16131614 if collection.access == AccessMode::WhiteList {1615 Self::check_white_list(&collection, &sender)?;1616 }16171618 match collection.mode1619 {1620 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1621 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1622 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1623 _ => ()1624 };16251626 Ok(())1627 }16281629 pub fn toggle_white_list_internal(1630 sender: &T::CrossAccountId,1631 collection: &CollectionHandle<T>,1632 address: &T::CrossAccountId,1633 whitelisted: bool,1634 ) -> DispatchResult {1635 Self::check_owner_or_admin_permissions(&collection, &sender)?;16361637 if whitelisted {1638 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1639 } else {1640 <WhiteList<T>>::remove(collection.id, address.as_sub());1641 }16421643 Ok(())1644 }16451646 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1647 let collection_id = collection.id;16481649 1650 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1651 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1652 1653 Ok(())1654 }16551656 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1657 let collection_id = collection.id;16581659 1660 let total_items: u32 = ItemListIndex::get(collection_id)1661 .checked_add(amount)1662 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1663 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1664 .checked_add(amount)1665 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1666 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1667 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);16681669 if !Self::is_owner_or_admin_permissions(collection, &sender) {1670 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1671 Self::check_white_list(collection, owner)?;1672 Self::check_white_list(collection, sender)?;1673 }16741675 Ok(())1676 }16771678 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1679 match target_collection.mode1680 {1681 CollectionMode::NFT => {1682 if let CreateItemData::NFT(data) = data {1683 1684 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1685 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1686 } else {1687 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1688 }1689 },1690 CollectionMode::Fungible(_) => {1691 if let CreateItemData::Fungible(_) = data {1692 } else {1693 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1694 }1695 },1696 CollectionMode::ReFungible => {1697 if let CreateItemData::ReFungible(data) = data {16981699 1700 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1701 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17021703 1704 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1705 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1706 } else {1707 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1708 }1709 },1710 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1711 };17121713 Ok(())1714 }17151716 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1717 match data1718 {1719 CreateItemData::NFT(data) => {1720 let item = NftItemType {1721 owner: owner.clone(),1722 const_data: data.const_data,1723 variable_data: data.variable_data1724 };17251726 Self::add_nft_item(collection, item)?;1727 },1728 CreateItemData::Fungible(data) => {1729 Self::add_fungible_item(collection, &owner, data.value)?;1730 },1731 CreateItemData::ReFungible(data) => {1732 let mut owner_list = Vec::new();1733 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17341735 let item = ReFungibleItemType {1736 owner: owner_list,1737 const_data: data.const_data,1738 variable_data: data.variable_data1739 };17401741 Self::add_refungible_item(collection, item)?;1742 }1743 };17441745 Ok(())1746 }17471748 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1749 let collection_id = collection.id;17501751 1752 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17531754 1755 let item = FungibleItemType {1756 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1757 };1758 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17591760 1761 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1762 .checked_add(value)1763 .ok_or(Error::<T>::NumOverflow)?;1764 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17651766 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1767 Ok(())1768 }17691770 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1771 let collection_id = collection.id;17721773 let current_index = <ItemListIndex>::get(collection_id)1774 .checked_add(1)1775 .ok_or(Error::<T>::NumOverflow)?;1776 let itemcopy = item.clone();17771778 ensure!(1779 item.owner.len() == 1,1780 Error::<T>::BadCreateRefungibleCall,1781 );1782 let item_owner = item.owner.first().expect("only one owner is defined");17831784 let value = item_owner.fraction;1785 let owner = item_owner.owner.clone();17861787 Self::add_token_index(collection_id, current_index, &owner)?;17881789 <ItemListIndex>::insert(collection_id, current_index);1790 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17911792 1793 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1794 .checked_add(value)1795 .ok_or(Error::<T>::NumOverflow)?;1796 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17971798 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1799 Ok(())1800 }18011802 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1803 let collection_id = collection.id;18041805 let current_index = <ItemListIndex>::get(collection_id)1806 .checked_add(1)1807 .ok_or(Error::<T>::NumOverflow)?;18081809 let item_owner = item.owner.clone();1810 Self::add_token_index(collection_id, current_index, &item.owner)?;18111812 <ItemListIndex>::insert(collection_id, current_index);1813 <NftItemList<T>>::insert(collection_id, current_index, item);18141815 1816 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1817 .checked_add(1)1818 .ok_or(Error::<T>::NumOverflow)?;1819 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18201821 collection.log(ERC721Events::Transfer {1822 from: H160::default(),1823 to: *item_owner.as_eth(),1824 token_id: current_index.into(),1825 });1826 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1827 Ok(())1828 }18291830 fn burn_refungible_item(1831 collection: &CollectionHandle<T>,1832 item_id: TokenId,1833 owner: &T::CrossAccountId,1834 ) -> DispatchResult {1835 let collection_id = collection.id;18361837 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1838 .ok_or(Error::<T>::TokenNotFound)?;1839 let rft_balance = token1840 .owner1841 .iter()1842 .find(|&i| i.owner == *owner)1843 .ok_or(Error::<T>::TokenNotFound)?;1844 Self::remove_token_index(collection_id, item_id, owner)?;18451846 1847 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1848 .checked_sub(rft_balance.fraction)1849 .ok_or(Error::<T>::NumOverflow)?;1850 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18511852 1853 let index = token1854 .owner1855 .iter()1856 .position(|i| i.owner == *owner)1857 .expect("owned item is exists");1858 token.owner.remove(index);1859 let owner_count = token.owner.len();18601861 1862 if owner_count == 0 {1863 <ReFungibleItemList<T>>::remove(collection_id, item_id);1864 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1865 }1866 else {1867 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1868 }18691870 Ok(())1871 }18721873 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1874 let collection_id = collection.id;18751876 let item = <NftItemList<T>>::get(collection_id, item_id)1877 .ok_or(Error::<T>::TokenNotFound)?;1878 Self::remove_token_index(collection_id, item_id, &item.owner)?;18791880 1881 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1882 .checked_sub(1)1883 .ok_or(Error::<T>::NumOverflow)?;1884 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1885 <NftItemList<T>>::remove(collection_id, item_id);1886 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18871888 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1889 Ok(())1890 }18911892 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> 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>::TokenValueNotEnough);18971898 1899 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1900 .checked_sub(value)1901 .ok_or(Error::<T>::NumOverflow)?;1902 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19031904 if balance.value - value > 0 {1905 balance.value -= value;1906 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1907 }1908 else {1909 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1910 }19111912 collection.log(ERC20Events::Transfer {1913 from: *owner.as_eth(),1914 to: H160::default(),1915 value: value.into(),1916 });1917 Ok(())1918 }19191920 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1921 Ok(<CollectionHandle<T>>::get(collection_id)1922 .ok_or(Error::<T>::CollectionNotFound)?)1923 }19241925 fn save_collection(collection: CollectionHandle<T>) {1926 <CollectionById<T>>::insert(collection.id, collection.into_inner());1927 }19281929 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1930 if collection.logs.is_empty() {1931 return Ok(())1932 }1933 T::EthereumTransactionSender::submit_logs_transaction(1934 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1935 collection.logs.retrieve_logs(),1936 )1937 }19381939 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1940 ensure!(1941 *subject == target_collection.owner,1942 Error::<T>::NoPermission1943 );19441945 Ok(())1946 }19471948 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1949 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1950 }19511952 fn check_owner_or_admin_permissions(1953 collection: &CollectionHandle<T>,1954 subject: &T::CrossAccountId,1955 ) -> DispatchResult {1956 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);19571958 Ok(())1959 }19601961 fn owned_amount(1962 subject: &T::CrossAccountId,1963 target_collection: &CollectionHandle<T>,1964 item_id: TokenId,1965 ) -> Option<u128> {1966 let collection_id = target_collection.id;19671968 match target_collection.mode {1969 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)1970 .then(|| 1),1971 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())1972 .value),1973 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1974 .owner1975 .iter()1976 .find(|i| i.owner == *subject)1977 .map(|i| i.fraction),1978 CollectionMode::Invalid => None,1979 }1980 }19811982 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1983 match target_collection.mode {1984 CollectionMode::Fungible(_) => true,1985 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),1986 }1987 }19881989 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {1990 let collection_id = collection.id;19911992 let mes = Error::<T>::AddresNotInWhiteList;1993 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);19941995 Ok(())1996 }19971998 1999 2000 fn token_exists(2001 target_collection: &CollectionHandle<T>,2002 item_id: TokenId,2003 ) -> DispatchResult {2004 let collection_id = target_collection.id;2005 let exists = match target_collection.mode2006 {2007 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2008 CollectionMode::Fungible(_) => true,2009 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2010 _ => false2011 };20122013 ensure!(exists == true, Error::<T>::TokenNotFound);2014 Ok(())2015 }20162017 fn transfer_fungible(2018 collection: &CollectionHandle<T>,2019 value: u128,2020 owner: &T::CrossAccountId,2021 recipient: &T::CrossAccountId,2022 ) -> DispatchResult {2023 let collection_id = collection.id;20242025 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2026 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20272028 2029 Self::add_fungible_item(collection, recipient, value)?;20302031 2032 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);20332034 2035 if balance.value == value {2036 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2037 }2038 else {2039 balance.value -= value;2040 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2041 }20422043 collection.log(ERC20Events::Transfer {2044 from: *owner.as_eth(),2045 to: *recipient.as_eth(),2046 value: value.into(),2047 });2048 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));20492050 Ok(())2051 }20522053 fn transfer_refungible(2054 collection: &CollectionHandle<T>,2055 item_id: TokenId,2056 value: u128,2057 owner: T::CrossAccountId,2058 new_owner: T::CrossAccountId,2059 ) -> DispatchResult {2060 let collection_id = collection.id;2061 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2062 .ok_or(Error::<T>::TokenNotFound)?;20632064 let item = full_item2065 .owner2066 .iter()2067 .filter(|i| i.owner == owner)2068 .next()2069 .ok_or(Error::<T>::TokenNotFound)?;2070 let amount = item.fraction;20712072 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20732074 2075 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2076 .checked_sub(value)2077 .ok_or(Error::<T>::NumOverflow)?;2078 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20792080 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2081 .checked_add(value)2082 .ok_or(Error::<T>::NumOverflow)?;2083 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20842085 let old_owner = item.owner.clone();2086 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20872088 2089 if amount == value && !new_owner_has_account {2090 2091 2092 let mut new_full_item = full_item.clone();2093 new_full_item2094 .owner2095 .iter_mut()2096 .find(|i| i.owner == owner)2097 .expect("old owner does present in refungible")2098 .owner = new_owner.clone();2099 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21002101 2102 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2103 } else {2104 let mut new_full_item = full_item.clone();2105 new_full_item2106 .owner2107 .iter_mut()2108 .find(|i| i.owner == owner)2109 .expect("old owner does present in refungible")2110 .fraction -= value;21112112 2113 if new_owner_has_account {2114 2115 new_full_item2116 .owner2117 .iter_mut()2118 .find(|i| i.owner == new_owner)2119 .expect("new owner has account")2120 .fraction += value;2121 } else {2122 2123 new_full_item.owner.push(Ownership {2124 owner: new_owner.clone(),2125 fraction: value,2126 });2127 Self::add_token_index(collection_id, item_id, &new_owner)?;2128 }21292130 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2131 }21322133 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));21342135 Ok(())2136 }21372138 fn transfer_nft(2139 collection: &CollectionHandle<T>,2140 item_id: TokenId,2141 sender: T::CrossAccountId,2142 new_owner: T::CrossAccountId,2143 ) -> DispatchResult {2144 let collection_id = collection.id;2145 let mut item = <NftItemList<T>>::get(collection_id, item_id)2146 .ok_or(Error::<T>::TokenNotFound)?;21472148 ensure!(2149 sender == item.owner,2150 Error::<T>::MustBeTokenOwner2151 );21522153 2154 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2155 .checked_sub(1)2156 .ok_or(Error::<T>::NumOverflow)?;2157 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21582159 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2160 .checked_add(1)2161 .ok_or(Error::<T>::NumOverflow)?;2162 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21632164 2165 let old_owner = item.owner.clone();2166 item.owner = new_owner.clone();2167 <NftItemList<T>>::insert(collection_id, item_id, item);21682169 2170 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21712172 collection.log(ERC721Events::Transfer {2173 from: *sender.as_eth(),2174 to: *new_owner.as_eth(),2175 token_id: item_id.into(),2176 });2177 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));21782179 Ok(())2180 }2181 2182 fn set_re_fungible_variable_data(2183 collection: &CollectionHandle<T>,2184 item_id: TokenId,2185 data: Vec<u8>2186 ) -> DispatchResult {2187 let collection_id = collection.id;2188 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2189 .ok_or(Error::<T>::TokenNotFound)?;21902191 item.variable_data = data;21922193 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21942195 Ok(())2196 }21972198 fn set_nft_variable_data(2199 collection: &CollectionHandle<T>,2200 item_id: TokenId,2201 data: Vec<u8>2202 ) -> DispatchResult {2203 let collection_id = collection.id;2204 let mut item = <NftItemList<T>>::get(collection_id, item_id)2205 .ok_or(Error::<T>::TokenNotFound)?;2206 2207 item.variable_data = data;22082209 <NftItemList<T>>::insert(collection_id, item_id, item);2210 2211 Ok(())2212 }22132214 #[allow(dead_code)]2215 fn init_collection(item: &Collection<T>) {2216 2217 assert!(2218 item.decimal_points <= MAX_DECIMAL_POINTS,2219 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2220 );2221 assert!(2222 item.name.len() <= 64,2223 "Collection name can not be longer than 63 char"2224 );2225 assert!(2226 item.name.len() <= 256,2227 "Collection description can not be longer than 255 char"2228 );2229 assert!(2230 item.token_prefix.len() <= 16,2231 "Token prefix can not be longer than 15 char"2232 );22332234 2235 let next_id = CreatedCollectionCount::get()2236 .checked_add(1)2237 .unwrap();22382239 CreatedCollectionCount::put(next_id);2240 }22412242 #[allow(dead_code)]2243 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2244 let current_index = <ItemListIndex>::get(collection_id)2245 .checked_add(1)2246 .unwrap();22472248 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22492250 <ItemListIndex>::insert(collection_id, current_index);22512252 2253 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2254 .checked_add(1)2255 .unwrap();2256 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2257 }22582259 #[allow(dead_code)]2260 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2261 let current_index = <ItemListIndex>::get(collection_id)2262 .checked_add(1)2263 .unwrap();22642265 Self::add_token_index(collection_id, current_index, owner).unwrap();22662267 <ItemListIndex>::insert(collection_id, current_index);22682269 2270 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2271 .checked_add(item.value)2272 .unwrap();2273 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2274 }22752276 #[allow(dead_code)]2277 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2278 let current_index = <ItemListIndex>::get(collection_id)2279 .checked_add(1)2280 .unwrap();22812282 let value = item.owner.first().unwrap().fraction;2283 let owner = item.owner.first().unwrap().owner.clone();22842285 Self::add_token_index(collection_id, current_index, &owner).unwrap();22862287 <ItemListIndex>::insert(collection_id, current_index);22882289 2290 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2291 .checked_add(value)2292 .unwrap();2293 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2294 }22952296 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2297 2298 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {22992300 2301 let count = <AccountItemCount<T>>::get(owner.as_sub());2302 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23032304 <AccountItemCount<T>>::insert(owner.as_sub(), count2305 .checked_add(1)2306 .ok_or(Error::<T>::NumOverflow)?);2307 }2308 else {2309 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2310 }23112312 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2313 if list_exists {2314 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2315 let item_contains = list.contains(&item_index.clone());23162317 if !item_contains {2318 list.push(item_index.clone());2319 }23202321 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2322 } else {2323 let mut itm = Vec::new();2324 itm.push(item_index.clone());2325 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2326 }23272328 Ok(())2329 }23302331 fn remove_token_index(2332 collection_id: CollectionId,2333 item_index: TokenId,2334 owner: &T::CrossAccountId,2335 ) -> DispatchResult {23362337 2338 <AccountItemCount<T>>::insert(owner.as_sub(), 2339 <AccountItemCount<T>>::get(owner.as_sub())2340 .checked_sub(1)2341 .ok_or(Error::<T>::NumOverflow)?);234223432344 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2345 if list_exists {2346 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2347 let item_contains = list.contains(&item_index.clone());23482349 if item_contains {2350 list.retain(|&item| item != item_index);2351 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2352 }2353 }23542355 Ok(())2356 }23572358 fn move_token_index(2359 collection_id: CollectionId,2360 item_index: TokenId,2361 old_owner: &T::CrossAccountId,2362 new_owner: &T::CrossAccountId,2363 ) -> DispatchResult {2364 Self::remove_token_index(collection_id, item_index, old_owner)?;2365 Self::add_token_index(collection_id, item_index, new_owner)?;23662367 Ok(())2368 }2369 2370 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2371 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);23722373 Ok(())2374 }2375}