123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16 construct_runtime, decl_event, decl_module, decl_storage, decl_error,17 dispatch::DispatchResult,18 ensure, fail, parameter_types,19 traits::{20 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21 Randomness, IsSubType, WithdrawReasons,22 },23 weights::{24 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26 WeightToFeePolynomial, DispatchClass,27 },28 StorageValue,29 transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36 traits::{37 Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38 },39 transaction_validity::{40 TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41 },42 FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_ethereum::EthereumTransactionSender;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::NftErcSupport;59pub use eth::account::*;6061pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;62pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;63pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;64pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6566676869pub type CollectionId = u32;70pub type TokenId = u32;71pub type DecimalPoints = u8;7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum CollectionMode {76 Invalid,77 NFT,78 79 Fungible(DecimalPoints),80 ReFungible,81}8283impl Default for CollectionMode {84 fn default() -> Self {85 Self::Invalid86 }87}8889impl Into<u8> for CollectionMode {90 fn into(self) -> u8 {91 match self {92 CollectionMode::Invalid => 0,93 CollectionMode::NFT => 1,94 CollectionMode::Fungible(_) => 2,95 CollectionMode::ReFungible => 3,96 }97 }98}99100#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub enum AccessMode {103 Normal,104 WhiteList,105}106impl Default for AccessMode {107 fn default() -> Self {108 Self::Normal109 }110}111112#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]113#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]114pub enum SchemaVersion {115 ImageURL,116 Unique,117}118impl Default for SchemaVersion {119 fn default() -> Self {120 Self::ImageURL121 }122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct Ownership<AccountId> {127 pub owner: AccountId,128 pub fraction: u128,129}130131#[derive(Encode, Decode, Debug, Clone, PartialEq)]132#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]133pub enum SponsorshipState<AccountId> {134 135 Disabled,136 Unconfirmed(AccountId),137 138 Confirmed(AccountId),139}140141impl<AccountId> SponsorshipState<AccountId> {142 fn sponsor(&self) -> Option<&AccountId> {143 match self {144 Self::Confirmed(sponsor) => Some(sponsor),145 _ => None,146 }147 }148149 fn pending_sponsor(&self) -> Option<&AccountId> {150 match self {151 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),152 _ => None,153 }154 }155156 fn confirmed(&self) -> bool {157 matches!(self, Self::Confirmed(_))158 }159}160161impl<T> Default for SponsorshipState<T> {162 fn default() -> Self {163 Self::Disabled164 }165}166167#[derive(Encode, Decode, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct Collection<T: Config> {170 pub owner: T::AccountId,171 pub mode: CollectionMode,172 pub access: AccessMode,173 pub decimal_points: DecimalPoints,174 pub name: Vec<u16>, 175 pub description: Vec<u16>, 176 pub token_prefix: Vec<u8>, 177 pub mint_mode: bool,178 pub offchain_schema: Vec<u8>,179 pub schema_version: SchemaVersion,180 pub sponsorship: SponsorshipState<T::AccountId>,181 pub limits: CollectionLimits<T::BlockNumber>, 182 pub variable_on_chain_schema: Vec<u8>, 183 pub const_on_chain_schema: Vec<u8>, 184}185186pub struct CollectionHandle<T: Config> {187 pub id: CollectionId,188 collection: Collection<T>,189 logs: eth::log::LogRecorder,190}191impl<T: Config> CollectionHandle<T> {192 pub fn get(id: CollectionId) -> Option<Self> {193 <CollectionById<T>>::get(id)194 .map(|collection| Self {195 id,196 collection,197 logs: eth::log::LogRecorder::default(),198 })199 }200 pub fn log(&self, topics: Vec<H256>, data: eth::abi::AbiWriter) {201 self.logs.log(topics, data)202 }203 pub fn into_inner(self) -> Collection<T> {204 self.collection.clone()205 }206}207208impl<T: Config> Deref for CollectionHandle<T> {209 type Target = Collection<T>;210211 fn deref(&self) -> &Self::Target {212 &self.collection213 }214}215216impl<T: Config> DerefMut for CollectionHandle<T> {217 fn deref_mut(&mut self) -> &mut Self::Target {218 &mut self.collection219 }220}221222#[derive(Encode, Decode, Debug, Clone, PartialEq)]223#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]224pub struct NftItemType<AccountId> {225 pub owner: AccountId,226 pub const_data: Vec<u8>,227 pub variable_data: Vec<u8>,228}229230#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]231#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]232pub struct FungibleItemType {233 pub value: u128,234}235236#[derive(Encode, Decode, Debug, Clone, PartialEq)]237#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]238pub struct ReFungibleItemType<AccountId> {239 pub owner: Vec<Ownership<AccountId>>,240 pub const_data: Vec<u8>,241 pub variable_data: Vec<u8>,242}243244245246247248249250251252253254255#[derive(Encode, Decode, Debug, Clone, PartialEq)]256#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]257pub struct CollectionLimits<BlockNumber: Encode + Decode> {258 pub account_token_ownership_limit: u32,259 pub sponsored_data_size: u32,260 261 262 263 pub sponsored_data_rate_limit: Option<BlockNumber>,264 pub token_limit: u32,265266 267 pub sponsor_transfer_timeout: u32,268 pub owner_can_transfer: bool,269 pub owner_can_destroy: bool,270}271272impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {273 fn default() -> Self {274 Self { 275 account_token_ownership_limit: 10_000_000, 276 token_limit: u32::max_value(),277 sponsored_data_size: u32::MAX, 278 sponsored_data_rate_limit: None,279 sponsor_transfer_timeout: 14400,280 owner_can_transfer: true,281 owner_can_destroy: true282 }283 }284}285286#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]287#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]288pub struct ChainLimits {289 pub collection_numbers_limit: u32,290 pub account_token_ownership_limit: u32,291 pub collections_admins_limit: u64,292 pub custom_data_limit: u32,293294 295 pub nft_sponsor_transfer_timeout: u32,296 pub fungible_sponsor_transfer_timeout: u32,297 pub refungible_sponsor_transfer_timeout: u32,298299 300 pub offchain_schema_limit: u32,301 pub variable_on_chain_schema_limit: u32,302 pub const_on_chain_schema_limit: u32,303}304305pub trait WeightInfo {306 fn create_collection() -> Weight;307 fn destroy_collection() -> Weight;308 fn add_to_white_list() -> Weight;309 fn remove_from_white_list() -> Weight;310 fn set_public_access_mode() -> Weight;311 fn set_mint_permission() -> Weight;312 fn change_collection_owner() -> Weight;313 fn add_collection_admin() -> Weight;314 fn remove_collection_admin() -> Weight;315 fn set_collection_sponsor() -> Weight;316 fn confirm_sponsorship() -> Weight;317 fn remove_collection_sponsor() -> Weight;318 fn create_item(s: usize) -> Weight;319 fn burn_item() -> Weight;320 fn transfer() -> Weight;321 fn approve() -> Weight;322 fn transfer_from() -> Weight;323 fn set_offchain_schema() -> Weight;324 fn set_const_on_chain_schema() -> Weight;325 fn set_variable_on_chain_schema() -> Weight;326 fn set_variable_meta_data() -> Weight;327 fn enable_contract_sponsoring() -> Weight;328 fn set_schema_version() -> Weight;329 fn set_chain_limits() -> Weight;330 fn set_contract_sponsoring_rate_limit() -> Weight;331 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;332 fn toggle_contract_white_list() -> Weight;333 fn add_to_contract_white_list() -> Weight;334 fn remove_from_contract_white_list() -> Weight;335 fn set_collection_limits() -> Weight;336}337338#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]339#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]340pub struct CreateNftData {341 pub const_data: Vec<u8>,342 pub variable_data: Vec<u8>,343}344345#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]346#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]347pub struct CreateFungibleData {348 pub value: u128,349}350351#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]352#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]353pub struct CreateReFungibleData {354 pub const_data: Vec<u8>,355 pub variable_data: Vec<u8>,356 pub pieces: u128,357}358359#[derive(Encode, Decode, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub enum CreateItemData {362 NFT(CreateNftData),363 Fungible(CreateFungibleData),364 ReFungible(CreateReFungibleData),365}366367impl CreateItemData {368 pub fn len(&self) -> usize {369 let len = match self {370 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),371 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),372 _ => 0373 };374 375 return len;376 }377}378379impl From<CreateNftData> for CreateItemData {380 fn from(item: CreateNftData) -> Self {381 CreateItemData::NFT(item)382 }383}384385impl From<CreateReFungibleData> for CreateItemData {386 fn from(item: CreateReFungibleData) -> Self {387 CreateItemData::ReFungible(item)388 }389}390391impl From<CreateFungibleData> for CreateItemData {392 fn from(item: CreateFungibleData) -> Self {393 CreateItemData::Fungible(item)394 }395}396397398decl_error! {399 400 pub enum Error for Module<T: Config> {401 402 TotalCollectionsLimitExceeded,403 404 CollectionDecimalPointLimitExceeded, 405 406 CollectionNameLimitExceeded, 407 408 CollectionDescriptionLimitExceeded, 409 410 CollectionTokenPrefixLimitExceeded,411 412 CollectionNotFound,413 414 TokenNotFound,415 416 AdminNotFound,417 418 NumOverflow, 419 420 AlreadyAdmin, 421 422 NoPermission,423 424 ConfirmUnsetSponsorFail,425 426 PublicMintingNotAllowed,427 428 MustBeTokenOwner,429 430 TokenValueTooLow,431 432 NftSizeLimitExceeded,433 434 ApproveNotFound,435 436 TokenValueNotEnough,437 438 ApproveRequired,439 440 AddresNotInWhiteList,441 442 CollectionAdminsLimitExceeded,443 444 AddressOwnershipLimitExceeded,445 446 EmptyArgument,447 448 TokenConstDataLimitExceeded,449 450 TokenVariableDataLimitExceeded,451 452 NotNftDataUsedToMintNftCollectionToken,453 454 NotFungibleDataUsedToMintFungibleCollectionToken,455 456 NotReFungibleDataUsedToMintReFungibleCollectionToken,457 458 UnexpectedCollectionType,459 460 CantStoreMetadataInFungibleTokens,461 462 CollectionTokenLimitExceeded,463 464 AccountTokenLimitExceeded,465 466 CollectionLimitBoundsExceeded,467 468 OwnerPermissionsCantBeReverted,469 470 SchemaDataLimitExceeded,471 472 WrongRefungiblePieces,473 474 BadCreateRefungibleCall,475 }476}477478pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {479 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;480481 482 type WeightInfo: WeightInfo;483484 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;485 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;486 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;487488 type CrossAccountId: CrossAccountId<Self::AccountId>;489 type Currency: Currency<Self::AccountId>;490 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;491 type TreasuryAccountId: Get<Self::AccountId>;492493 type EthereumChainId: Get<u64>;494 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;495}496497#[cfg(feature = "runtime-benchmarks")]498mod benchmarking;499500501502503504505506507508509510511512513514515516517518519520521522523524decl_storage! {525 trait Store for Module<T: Config> as Nft {526527 528 529 CreatedCollectionCount: u32;530 531 ChainVersion: u64;532 533 534 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;535 536537 538 pub ChainLimit get(fn chain_limit) config(): ChainLimits;539 540541 542 543 544 DestroyedCollectionCount: u32;545 546 547 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;548 549550 551 552 553 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;554 555 556 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;557 558 559 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;560 561562 563 564 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;565566 567 568 569 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;570571 572 573 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;574 575 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;576 577 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;578 579580 581 582 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;583 584585 586 587 588 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;589 590 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;591 592 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;593 594 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;595 596597 598 599 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;600 601 602 603 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;604 605 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;606 607 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;608 609 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;610 611 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 612 613 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 614 615 }616 add_extra_genesis {617 build(|config: &GenesisConfig<T>| {618 619 for (_num, _c) in &config.collection_id {620 <Module<T>>::init_collection(_c);621 }622623 for (_num, _c, _i) in &config.nft_item_id {624 <Module<T>>::init_nft_token(*_c, _i);625 }626627 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {628 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);629 }630631 for (_num, _c, _i) in &config.refungible_item_id {632 <Module<T>>::init_refungible_token(*_c, _i);633 }634 })635 }636}637638decl_event!(639 pub enum Event<T>640 where641 AccountId = <T as frame_system::Config>::AccountId,642 CrossAccountId = <T as Config>::CrossAccountId,643 {644 645 646 647 648 649 650 651 652 653 CollectionCreated(CollectionId, u8, AccountId),654655 656 657 658 659 660 661 662 663 664 ItemCreated(CollectionId, TokenId, CrossAccountId),665666 667 668 669 670 671 672 673 ItemDestroyed(CollectionId, TokenId),674675 676 677 678 679 680 681 682 683 684 685 686 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),687688 689 690 691 692 693 694 695 696 697 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),698 }699);700701decl_module! {702 pub struct Module<T: Config> for enum Call 703 where 704 origin: T::Origin705 {706 fn deposit_event() = default;707 type Error = Error<T>;708709 fn on_initialize(now: T::BlockNumber) -> Weight {710 0711 }712713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 #[weight = <T as Config>::WeightInfo::create_collection()]730 #[transactional]731 pub fn create_collection(origin,732 collection_name: Vec<u16>,733 collection_description: Vec<u16>,734 token_prefix: Vec<u8>,735 mode: CollectionMode) -> DispatchResult {736737 738 let who = ensure_signed(origin)?;739740 741 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();742 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(743 &T::TreasuryAccountId::get(),744 T::CollectionCreationPrice::get(),745 ));746 <T as Config>::Currency::settle(747 &who,748 imbalance,749 WithdrawReasons::TRANSFER,750 ExistenceRequirement::KeepAlive,751 ).map_err(|_| Error::<T>::NoPermission)?;752753 let decimal_points = match mode {754 CollectionMode::Fungible(points) => points,755 _ => 0756 };757758 let chain_limit = ChainLimit::get();759760 let created_count = CreatedCollectionCount::get();761 let destroyed_count = DestroyedCollectionCount::get();762763 764 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);765766 767 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);768 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);769 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);770 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);771772 773 let next_id = created_count774 .checked_add(1)775 .ok_or(Error::<T>::NumOverflow)?;776777 CreatedCollectionCount::put(next_id);778779 let limits = CollectionLimits {780 sponsored_data_size: chain_limit.custom_data_limit,781 ..Default::default()782 };783784 785 let new_collection = Collection {786 owner: who.clone(),787 name: collection_name,788 mode: mode.clone(),789 mint_mode: false,790 access: AccessMode::Normal,791 description: collection_description,792 decimal_points: decimal_points,793 token_prefix: token_prefix,794 offchain_schema: Vec::new(),795 schema_version: SchemaVersion::ImageURL,796 sponsorship: SponsorshipState::Disabled,797 variable_on_chain_schema: Vec::new(),798 const_on_chain_schema: Vec::new(),799 limits,800 };801802 803 <CollectionById<T>>::insert(next_id, new_collection);804805 806 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));807808 Ok(())809 }810811 812 813 814 815 816 817 818 819 820 #[weight = <T as Config>::WeightInfo::destroy_collection()]821 #[transactional]822 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {823824 let sender = ensure_signed(origin)?;825 let collection = Self::get_collection(collection_id)?;826 Self::check_owner_permissions(&collection, &sender)?;827 if !collection.limits.owner_can_destroy {828 fail!(Error::<T>::NoPermission);829 }830831 <AddressTokens<T>>::remove_prefix(collection_id);832 <Allowances<T>>::remove_prefix(collection_id);833 <Balance<T>>::remove_prefix(collection_id);834 <ItemListIndex>::remove(collection_id);835 <AdminList<T>>::remove(collection_id);836 <CollectionById<T>>::remove(collection_id);837 <WhiteList<T>>::remove_prefix(collection_id);838839 <NftItemList<T>>::remove_prefix(collection_id);840 <FungibleItemList<T>>::remove_prefix(collection_id);841 <ReFungibleItemList<T>>::remove_prefix(collection_id);842843 <NftTransferBasket<T>>::remove_prefix(collection_id);844 <FungibleTransferBasket<T>>::remove_prefix(collection_id);845 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);846847 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);848849 DestroyedCollectionCount::put(DestroyedCollectionCount::get()850 .checked_add(1)851 .ok_or(Error::<T>::NumOverflow)?);852853 Ok(())854 }855856 857 858 859 860 861 862 863 864 865 866 867 868 #[weight = <T as Config>::WeightInfo::add_to_white_list()]869 #[transactional]870 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{871872 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);873 let collection = Self::get_collection(collection_id)?;874875 Self::toggle_white_list_internal(876 &sender,877 &collection,878 &address,879 true,880 )?;881882 Ok(())883 }884885 886 887 888 889 890 891 892 893 894 895 896 897 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]898 #[transactional]899 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{900901 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902 let collection = Self::get_collection(collection_id)?;903904 Self::toggle_white_list_internal(905 &sender,906 &collection,907 &address,908 false,909 )?;910911 Ok(())912 }913914 915 916 917 918 919 920 921 922 923 924 925 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]926 #[transactional]927 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult928 {929 let sender = ensure_signed(origin)?;930931 let mut target_collection = Self::get_collection(collection_id)?;932 Self::check_owner_permissions(&target_collection, &sender)?;933 target_collection.access = mode;934 Self::save_collection(target_collection);935936 Ok(())937 }938939 940 941 942 943 944 945 946 947 948 949 950 951 952 #[weight = <T as Config>::WeightInfo::set_mint_permission()]953 #[transactional]954 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult955 {956 let sender = ensure_signed(origin)?;957958 let mut target_collection = Self::get_collection(collection_id)?;959 Self::check_owner_permissions(&target_collection, &sender)?;960 target_collection.mint_mode = mint_permission;961 Self::save_collection(target_collection);962963 Ok(())964 }965966 967 968 969 970 971 972 973 974 975 976 977 #[weight = <T as Config>::WeightInfo::change_collection_owner()]978 #[transactional]979 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {980981 let sender = ensure_signed(origin)?;982 let mut target_collection = Self::get_collection(collection_id)?;983 Self::check_owner_permissions(&target_collection, &sender)?;984 target_collection.owner = new_owner;985 Self::save_collection(target_collection);986987 Ok(())988 }989990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 #[weight = <T as Config>::WeightInfo::add_collection_admin()]1004 #[transactional]1005 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1006 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1007 let collection = Self::get_collection(collection_id)?;1008 Self::check_owner_or_admin_permissions(&collection, &sender)?;1009 let mut admin_arr = <AdminList<T>>::get(collection_id);10101011 match admin_arr.binary_search(&new_admin_id) {1012 Ok(_) => {},1013 Err(idx) => {1014 let limits = ChainLimit::get();1015 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1016 admin_arr.insert(idx, new_admin_id);1017 <AdminList<T>>::insert(collection_id, admin_arr);1018 }1019 }1020 Ok(())1021 }10221023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1036 #[transactional]1037 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1038 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039 let collection = Self::get_collection(collection_id)?;1040 Self::check_owner_or_admin_permissions(&collection, &sender)?;1041 let mut admin_arr = <AdminList<T>>::get(collection_id);10421043 match admin_arr.binary_search(&account_id) {1044 Ok(idx) => {1045 admin_arr.remove(idx);1046 <AdminList<T>>::insert(collection_id, admin_arr);1047 },1048 Err(_) => {}1049 }1050 Ok(())1051 }10521053 1054 1055 1056 1057 1058 1059 1060 1061 1062 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1063 #[transactional]1064 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1065 let sender = ensure_signed(origin)?;1066 let mut target_collection = Self::get_collection(collection_id)?;1067 Self::check_owner_permissions(&target_collection, &sender)?;10681069 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1070 Self::save_collection(target_collection);10711072 Ok(())1073 }10741075 1076 1077 1078 1079 1080 1081 1082 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1083 #[transactional]1084 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1085 let sender = ensure_signed(origin)?;10861087 let mut target_collection = Self::get_collection(collection_id)?;1088 ensure!(1089 target_collection.sponsorship.pending_sponsor() == Some(&sender),1090 Error::<T>::ConfirmUnsetSponsorFail1091 );10921093 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1094 Self::save_collection(target_collection);10951096 Ok(())1097 }10981099 1100 1101 1102 1103 1104 1105 1106 1107 1108 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1109 #[transactional]1110 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1111 let sender = ensure_signed(origin)?;11121113 let mut target_collection = Self::get_collection(collection_id)?;1114 Self::check_owner_permissions(&target_collection, &sender)?;11151116 target_collection.sponsorship = SponsorshipState::Disabled;1117 Self::save_collection(target_collection);11181119 Ok(())1120 }11211122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 11451146 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1147 #[transactional]1148 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1149 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1150 let collection = Self::get_collection(collection_id)?;11511152 Self::create_item_internal(&sender, &collection, &owner, data)?;11531154 Self::submit_logs(collection)?;1155 Ok(())1156 }11571158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1177 .map(|data| { data.len() })1178 .sum())]1179 #[transactional]1180 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11811182 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1183 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1184 let collection = Self::get_collection(collection_id)?;11851186 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11871188 Self::submit_logs(collection)?;1189 Ok(())1190 }11911192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 #[weight = <T as Config>::WeightInfo::burn_item()]1206 #[transactional]1207 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12081209 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210 let target_collection = Self::get_collection(collection_id)?;12111212 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12131214 Self::submit_logs(target_collection)?;1215 Ok(())1216 }12171218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 #[weight = <T as Config>::WeightInfo::transfer()]1242 #[transactional]1243 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1244 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1245 let collection = Self::get_collection(collection_id)?;12461247 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12481249 Self::submit_logs(collection)?;1250 Ok(())1251 }12521253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 #[weight = <T as Config>::WeightInfo::approve()]1269 #[transactional]1270 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1271 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1272 let collection = Self::get_collection(collection_id)?;12731274 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12751276 Self::submit_logs(collection)?;1277 Ok(())1278 }1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 #[weight = <T as Config>::WeightInfo::transfer_from()]1300 #[transactional]1301 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1302 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1303 let collection = Self::get_collection(collection_id)?;13041305 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13061307 Self::submit_logs(collection)?;1308 Ok(())1309 }1310 1311 1312 1313 1314 13151316 13171318 13191320 1321 13221323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1336 #[transactional]1337 pub fn set_variable_meta_data (1338 origin,1339 collection_id: CollectionId,1340 item_id: TokenId,1341 data: Vec<u8>1342 ) -> DispatchResult {1343 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1344 1345 let collection = Self::get_collection(collection_id)?;13461347 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13481349 Ok(())1350 }1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 #[weight = <T as Config>::WeightInfo::set_schema_version()]1367 #[transactional]1368 pub fn set_schema_version(1369 origin,1370 collection_id: CollectionId,1371 version: SchemaVersion1372 ) -> DispatchResult {1373 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1374 let mut target_collection = Self::get_collection(collection_id)?;1375 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1376 target_collection.schema_version = version;1377 Self::save_collection(target_collection);13781379 Ok(())1380 }13811382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1395 #[transactional]1396 pub fn set_offchain_schema(1397 origin,1398 collection_id: CollectionId,1399 schema: Vec<u8>1400 ) -> DispatchResult {1401 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1402 let mut target_collection = Self::get_collection(collection_id)?;1403 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14041405 1406 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14071408 target_collection.offchain_schema = schema;1409 Self::save_collection(target_collection);14101411 Ok(())1412 }14131414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1427 #[transactional]1428 pub fn set_const_on_chain_schema (1429 origin,1430 collection_id: CollectionId,1431 schema: Vec<u8>1432 ) -> DispatchResult {1433 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1434 let mut target_collection = Self::get_collection(collection_id)?;1435 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14361437 1438 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14391440 target_collection.const_on_chain_schema = schema;1441 Self::save_collection(target_collection);14421443 Ok(())1444 }14451446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1459 #[transactional]1460 pub fn set_variable_on_chain_schema (1461 origin,1462 collection_id: CollectionId,1463 schema: Vec<u8>1464 ) -> DispatchResult {1465 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1466 let mut target_collection = Self::get_collection(collection_id)?;1467 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14681469 1470 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14711472 target_collection.variable_on_chain_schema = schema;1473 Self::save_collection(target_collection);14741475 Ok(())1476 }14771478 1479 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1480 #[transactional]1481 pub fn set_chain_limits(1482 origin,1483 limits: ChainLimits1484 ) -> DispatchResult {14851486 #[cfg(not(feature = "runtime-benchmarks"))]1487 ensure_root(origin)?;14881489 <ChainLimit>::put(limits);1490 Ok(())1491 }14921493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1505 #[transactional]1506 pub fn enable_contract_sponsoring(1507 origin,1508 contract_address: T::AccountId,1509 enable: bool1510 ) -> DispatchResult {15111512 let sender = ensure_signed(origin)?;15131514 #[cfg(feature = "runtime-benchmarks")]1515 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15161517 Self::ensure_contract_owned(sender, &contract_address)?;15181519 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1520 Ok(())1521 }15221523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1541 #[transactional]1542 pub fn set_contract_sponsoring_rate_limit(1543 origin,1544 contract_address: T::AccountId,1545 rate_limit: T::BlockNumber1546 ) -> DispatchResult {1547 let sender = ensure_signed(origin)?;15481549 #[cfg(feature = "runtime-benchmarks")]1550 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15511552 Self::ensure_contract_owned(sender, &contract_address)?;1553 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1554 Ok(())1555 }15561557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1569 #[transactional]1570 pub fn toggle_contract_white_list(1571 origin,1572 contract_address: T::AccountId,1573 enable: bool1574 ) -> DispatchResult {1575 let sender = ensure_signed(origin)?;15761577 #[cfg(feature = "runtime-benchmarks")]1578 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15791580 Self::ensure_contract_owned(sender, &contract_address)?;1581 if enable {1582 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1583 } else {1584 <ContractWhiteListEnabled<T>>::remove(contract_address);1585 }1586 Ok(())1587 }1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1601 #[transactional]1602 pub fn add_to_contract_white_list(1603 origin,1604 contract_address: T::AccountId,1605 account_address: T::AccountId1606 ) -> DispatchResult {1607 let sender = ensure_signed(origin)?;16081609 #[cfg(feature = "runtime-benchmarks")]1610 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1611 1612 Self::ensure_contract_owned(sender, &contract_address)?; 1613 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1614 Ok(())1615 }16161617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1629 #[transactional]1630 pub fn remove_from_contract_white_list(1631 origin,1632 contract_address: T::AccountId,1633 account_address: T::AccountId1634 ) -> DispatchResult {1635 let sender = ensure_signed(origin)?;16361637 #[cfg(feature = "runtime-benchmarks")]1638 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16391640 Self::ensure_contract_owned(sender, &contract_address)?;1641 <ContractWhiteList<T>>::remove(contract_address, account_address);1642 Ok(())1643 }16441645 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1646 #[transactional]1647 pub fn set_collection_limits(1648 origin,1649 collection_id: u32,1650 new_limits: CollectionLimits<T::BlockNumber>,1651 ) -> DispatchResult {1652 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1653 let mut target_collection = Self::get_collection(collection_id)?;1654 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1655 let old_limits = &target_collection.limits;1656 let chain_limits = ChainLimit::get();16571658 1659 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1660 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1661 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1662 Error::<T>::CollectionLimitBoundsExceeded);16631664 1665 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1666 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16671668 ensure!(1669 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1670 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1671 Error::<T>::OwnerPermissionsCantBeReverted,1672 );16731674 target_collection.limits = new_limits;1675 Self::save_collection(target_collection);16761677 Ok(())1678 } 1679 }1680}16811682impl<T: Config> Module<T> {1683 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1684 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1685 Self::validate_create_item_args(&collection, &data)?;1686 Self::create_item_no_validation(&collection, owner, data)?;16871688 Ok(())1689 }16901691 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1692 1693 Self::is_correct_transfer(target_collection, &recipient)?;16941695 1696 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1697 Self::is_owner_or_admin_permissions(target_collection, &sender),1698 Error::<T>::NoPermission);16991700 if target_collection.access == AccessMode::WhiteList {1701 Self::check_white_list(target_collection, &sender)?;1702 Self::check_white_list(target_collection, &recipient)?;1703 }17041705 match target_collection.mode1706 {1707 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1708 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1709 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1710 _ => ()1711 };17121713 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17141715 Ok(())1716 }17171718 pub fn approve_internal(1719 sender: &T::CrossAccountId,1720 spender: &T::CrossAccountId,1721 collection: &CollectionHandle<T>,1722 item_id: TokenId,1723 amount: u1281724 ) -> DispatchResult {1725 Self::token_exists(&collection, item_id)?;17261727 1728 let bypasses_limits = collection.limits.owner_can_transfer &&1729 Self::is_owner_or_admin_permissions(1730 &collection,1731 &sender,1732 );17331734 let allowance_limit = if bypasses_limits {1735 None1736 } else if let Some(amount) = Self::owned_amount(1737 &sender,1738 &collection,1739 item_id,1740 ) {1741 Some(amount)1742 } else {1743 fail!(Error::<T>::NoPermission);1744 };17451746 if collection.access == AccessMode::WhiteList {1747 Self::check_white_list(&collection, &sender)?;1748 Self::check_white_list(&collection, &spender)?;1749 }17501751 let allowance: u128 = amount1752 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1753 .ok_or(Error::<T>::NumOverflow)?;1754 if let Some(limit) = allowance_limit {1755 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1756 }1757 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17581759 if matches!(collection.mode, CollectionMode::NFT) {1760 1761 collection.log(1762 Vec::from([1763 eth::APPROVAL_NFT_TOPIC,1764 eth::address_to_topic(sender.as_eth()),1765 eth::address_to_topic(spender.as_eth()),1766 eth::u32_to_topic(item_id),1767 ]),1768 abi_encode!(),1769 );1770 }17711772 if matches!(collection.mode, CollectionMode::Fungible(_)) {1773 1774 collection.log(1775 Vec::from([1776 eth::APPROVAL_FUNGIBLE_TOPIC,1777 eth::address_to_topic(sender.as_eth()),1778 eth::address_to_topic(spender.as_eth()),1779 ]),1780 abi_encode!(uint256(allowance.into())),1781 );1782 }17831784 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1785 Ok(())1786 }17871788 pub fn transfer_from_internal(1789 sender: &T::CrossAccountId,1790 from: &T::CrossAccountId,1791 recipient: &T::CrossAccountId,1792 collection: &CollectionHandle<T>,1793 item_id: TokenId,1794 amount: u128,1795 ) -> DispatchResult {1796 1797 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17981799 1800 Self::is_correct_transfer(&collection, &recipient)?;18011802 1803 ensure!(1804 approval >= amount || 1805 (1806 collection.limits.owner_can_transfer &&1807 Self::is_owner_or_admin_permissions(&collection, &sender)1808 ),1809 Error::<T>::NoPermission1810 );18111812 if collection.access == AccessMode::WhiteList {1813 Self::check_white_list(&collection, &sender)?;1814 Self::check_white_list(&collection, &recipient)?;1815 }18161817 1818 let allowance = approval.saturating_sub(amount);1819 if allowance > 0 {1820 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1821 } else {1822 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1823 }18241825 match collection.mode {1826 CollectionMode::NFT => {1827 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1828 }1829 CollectionMode::Fungible(_) => {1830 Self::transfer_fungible(&collection, amount, &from, &recipient)?1831 }1832 CollectionMode::ReFungible => {1833 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1834 }1835 _ => ()1836 };18371838 if matches!(collection.mode, CollectionMode::Fungible(_)) {1839 collection.log(1840 Vec::from([1841 eth::APPROVAL_FUNGIBLE_TOPIC,1842 eth::address_to_topic(from.as_eth()),1843 eth::address_to_topic(sender.as_eth()),1844 ]),1845 abi_encode!(uint256(allowance.into())),1846 );1847 }18481849 Ok(())1850 }18511852 pub fn set_variable_meta_data_internal(1853 sender: &T::CrossAccountId,1854 collection: &CollectionHandle<T>, 1855 item_id: TokenId,1856 data: Vec<u8>,1857 ) -> DispatchResult {1858 Self::token_exists(&collection, item_id)?;18591860 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18611862 1863 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1864 Self::is_owner_or_admin_permissions(&collection, &sender),1865 Error::<T>::NoPermission);18661867 match collection.mode1868 {1869 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1870 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1871 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1872 _ => fail!(Error::<T>::UnexpectedCollectionType)1873 };18741875 Ok(())1876 }18771878 pub fn create_multiple_items_internal(1879 sender: &T::CrossAccountId,1880 collection: &CollectionHandle<T>,1881 owner: &T::CrossAccountId,1882 items_data: Vec<CreateItemData>,1883 ) -> DispatchResult {1884 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18851886 for data in &items_data {1887 Self::validate_create_item_args(&collection, data)?;1888 }1889 for data in &items_data {1890 Self::create_item_no_validation(&collection, owner, data.clone())?;1891 }18921893 Ok(())1894 }18951896 pub fn burn_item_internal(1897 sender: &T::CrossAccountId,1898 collection: &CollectionHandle<T>,1899 item_id: TokenId,1900 value: u128,1901 ) -> DispatchResult {1902 ensure!(1903 Self::is_item_owner(&sender, &collection, item_id) ||1904 (1905 collection.limits.owner_can_transfer &&1906 Self::is_owner_or_admin_permissions(&collection, &sender)1907 ),1908 Error::<T>::NoPermission1909 );19101911 if collection.access == AccessMode::WhiteList {1912 Self::check_white_list(&collection, &sender)?;1913 }19141915 match collection.mode1916 {1917 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1918 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1919 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1920 _ => ()1921 };19221923 Ok(())1924 }19251926 pub fn toggle_white_list_internal(1927 sender: &T::CrossAccountId,1928 collection: &CollectionHandle<T>,1929 address: &T::CrossAccountId,1930 whitelisted: bool,1931 ) -> DispatchResult {1932 Self::check_owner_or_admin_permissions(&collection, &sender)?;19331934 if whitelisted {1935 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1936 } else {1937 <WhiteList<T>>::remove(collection.id, address.as_sub());1938 }19391940 Ok(())1941 }19421943 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1944 let collection_id = collection.id;19451946 1947 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1948 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1949 1950 Ok(())1951 }19521953 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1954 let collection_id = collection.id;19551956 1957 let total_items: u32 = ItemListIndex::get(collection_id)1958 .checked_add(amount)1959 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1960 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1961 .checked_add(amount)1962 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1963 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1964 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19651966 if !Self::is_owner_or_admin_permissions(collection, &sender) {1967 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1968 Self::check_white_list(collection, owner)?;1969 Self::check_white_list(collection, sender)?;1970 }19711972 Ok(())1973 }19741975 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1976 match target_collection.mode1977 {1978 CollectionMode::NFT => {1979 if let CreateItemData::NFT(data) = data {1980 1981 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1982 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1983 } else {1984 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1985 }1986 },1987 CollectionMode::Fungible(_) => {1988 if let CreateItemData::Fungible(_) = data {1989 } else {1990 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1991 }1992 },1993 CollectionMode::ReFungible => {1994 if let CreateItemData::ReFungible(data) = data {19951996 1997 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1998 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19992000 2001 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2002 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2003 } else {2004 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2005 }2006 },2007 _ => { fail!(Error::<T>::UnexpectedCollectionType); }2008 };20092010 Ok(())2011 }20122013 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2014 match data2015 {2016 CreateItemData::NFT(data) => {2017 let item = NftItemType {2018 owner: owner.clone(),2019 const_data: data.const_data,2020 variable_data: data.variable_data2021 };20222023 Self::add_nft_item(collection, item)?;2024 },2025 CreateItemData::Fungible(data) => {2026 Self::add_fungible_item(collection, &owner, data.value)?;2027 },2028 CreateItemData::ReFungible(data) => {2029 let mut owner_list = Vec::new();2030 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20312032 let item = ReFungibleItemType {2033 owner: owner_list,2034 const_data: data.const_data,2035 variable_data: data.variable_data2036 };20372038 Self::add_refungible_item(collection, item)?;2039 }2040 };20412042 Ok(())2043 }20442045 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2046 let collection_id = collection.id;20472048 2049 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20502051 2052 let item = FungibleItemType {2053 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2054 };2055 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20562057 2058 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2059 .checked_add(value)2060 .ok_or(Error::<T>::NumOverflow)?;2061 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20622063 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2064 Ok(())2065 }20662067 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2068 let collection_id = collection.id;20692070 let current_index = <ItemListIndex>::get(collection_id)2071 .checked_add(1)2072 .ok_or(Error::<T>::NumOverflow)?;2073 let itemcopy = item.clone();20742075 ensure!(2076 item.owner.len() == 1,2077 Error::<T>::BadCreateRefungibleCall,2078 );2079 let item_owner = item.owner.first().expect("only one owner is defined");20802081 let value = item_owner.fraction;2082 let owner = item_owner.owner.clone();20832084 Self::add_token_index(collection_id, current_index, &owner)?;20852086 <ItemListIndex>::insert(collection_id, current_index);2087 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20882089 2090 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2091 .checked_add(value)2092 .ok_or(Error::<T>::NumOverflow)?;2093 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20942095 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2096 Ok(())2097 }20982099 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2100 let collection_id = collection.id;21012102 let current_index = <ItemListIndex>::get(collection_id)2103 .checked_add(1)2104 .ok_or(Error::<T>::NumOverflow)?;21052106 let item_owner = item.owner.clone();2107 Self::add_token_index(collection_id, current_index, &item.owner)?;21082109 <ItemListIndex>::insert(collection_id, current_index);2110 <NftItemList<T>>::insert(collection_id, current_index, item);21112112 2113 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2114 .checked_add(1)2115 .ok_or(Error::<T>::NumOverflow)?;2116 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21172118 collection.log(2119 Vec::from([2120 eth::TRANSFER_NFT_TOPIC,2121 eth::address_to_topic(&H160::default()),2122 eth::address_to_topic(item_owner.as_eth()),2123 eth::u32_to_topic(current_index),2124 ]),2125 abi_encode!(),2126 );2127 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2128 Ok(())2129 }21302131 fn burn_refungible_item(2132 collection: &CollectionHandle<T>,2133 item_id: TokenId,2134 owner: &T::CrossAccountId,2135 ) -> DispatchResult {2136 let collection_id = collection.id;21372138 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2139 .ok_or(Error::<T>::TokenNotFound)?;2140 let rft_balance = token2141 .owner2142 .iter()2143 .find(|&i| i.owner == *owner)2144 .ok_or(Error::<T>::TokenNotFound)?;2145 Self::remove_token_index(collection_id, item_id, owner)?;21462147 2148 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2149 .checked_sub(rft_balance.fraction)2150 .ok_or(Error::<T>::NumOverflow)?;2151 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21522153 2154 let index = token2155 .owner2156 .iter()2157 .position(|i| i.owner == *owner)2158 .expect("owned item is exists");2159 token.owner.remove(index);2160 let owner_count = token.owner.len();21612162 2163 if owner_count == 0 {2164 <ReFungibleItemList<T>>::remove(collection_id, item_id);2165 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2166 }2167 else {2168 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2169 }21702171 Ok(())2172 }21732174 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2175 let collection_id = collection.id;21762177 let item = <NftItemList<T>>::get(collection_id, item_id)2178 .ok_or(Error::<T>::TokenNotFound)?;2179 Self::remove_token_index(collection_id, item_id, &item.owner)?;21802181 2182 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2183 .checked_sub(1)2184 .ok_or(Error::<T>::NumOverflow)?;2185 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2186 <NftItemList<T>>::remove(collection_id, item_id);2187 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21882189 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2190 Ok(())2191 }21922193 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2194 let collection_id = collection.id;21952196 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2197 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21982199 2200 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2201 .checked_sub(value)2202 .ok_or(Error::<T>::NumOverflow)?;2203 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22042205 if balance.value - value > 0 {2206 balance.value -= value;2207 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2208 }2209 else {2210 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2211 }22122213 collection.log(2214 Vec::from([2215 eth::TRANSFER_FUNGIBLE_TOPIC,2216 eth::address_to_topic(owner.as_eth()),2217 eth::address_to_topic(&H160::default()),2218 ]),2219 abi_encode!(uint256(value.into())),2220 );2221 Ok(())2222 }22232224 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2225 Ok(<CollectionHandle<T>>::get(collection_id)2226 .ok_or(Error::<T>::CollectionNotFound)?)2227 }22282229 fn save_collection(collection: CollectionHandle<T>) {2230 <CollectionById<T>>::insert(collection.id, collection.into_inner());2231 }22322233 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2234 if collection.logs.is_empty() {2235 return Ok(())2236 }2237 T::EthereumTransactionSender::submit_logs_transaction(2238 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2239 collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2240 )2241 }22422243 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2244 ensure!(2245 *subject == target_collection.owner,2246 Error::<T>::NoPermission2247 );22482249 Ok(())2250 }22512252 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2253 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2254 }22552256 fn check_owner_or_admin_permissions(2257 collection: &CollectionHandle<T>,2258 subject: &T::CrossAccountId,2259 ) -> DispatchResult {2260 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22612262 Ok(())2263 }22642265 fn owned_amount(2266 subject: &T::CrossAccountId,2267 target_collection: &CollectionHandle<T>,2268 item_id: TokenId,2269 ) -> Option<u128> {2270 let collection_id = target_collection.id;22712272 match target_collection.mode {2273 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2274 .then(|| 1),2275 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2276 .value),2277 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2278 .owner2279 .iter()2280 .find(|i| i.owner == *subject)2281 .map(|i| i.fraction),2282 CollectionMode::Invalid => None,2283 }2284 }22852286 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2287 match target_collection.mode {2288 CollectionMode::Fungible(_) => true,2289 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2290 }2291 }22922293 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2294 let collection_id = collection.id;22952296 let mes = Error::<T>::AddresNotInWhiteList;2297 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22982299 Ok(())2300 }23012302 2303 2304 fn token_exists(2305 target_collection: &CollectionHandle<T>,2306 item_id: TokenId,2307 ) -> DispatchResult {2308 let collection_id = target_collection.id;2309 let exists = match target_collection.mode2310 {2311 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2312 CollectionMode::Fungible(_) => true,2313 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2314 _ => false2315 };23162317 ensure!(exists == true, Error::<T>::TokenNotFound);2318 Ok(())2319 }23202321 fn transfer_fungible(2322 collection: &CollectionHandle<T>,2323 value: u128,2324 owner: &T::CrossAccountId,2325 recipient: &T::CrossAccountId,2326 ) -> DispatchResult {2327 let collection_id = collection.id;23282329 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2330 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23312332 2333 Self::add_fungible_item(collection, recipient, value)?;23342335 2336 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23372338 2339 if balance.value == value {2340 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2341 }2342 else {2343 balance.value -= value;2344 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2345 }23462347 collection.log(2348 Vec::from([2349 eth::TRANSFER_FUNGIBLE_TOPIC,2350 eth::address_to_topic(owner.as_eth()),2351 eth::address_to_topic(recipient.as_eth()),2352 ]),2353 abi_encode!(uint256(value.into())),2354 );2355 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23562357 Ok(())2358 }23592360 fn transfer_refungible(2361 collection: &CollectionHandle<T>,2362 item_id: TokenId,2363 value: u128,2364 owner: T::CrossAccountId,2365 new_owner: T::CrossAccountId,2366 ) -> DispatchResult {2367 let collection_id = collection.id;2368 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2369 .ok_or(Error::<T>::TokenNotFound)?;23702371 let item = full_item2372 .owner2373 .iter()2374 .filter(|i| i.owner == owner)2375 .next()2376 .ok_or(Error::<T>::TokenNotFound)?;2377 let amount = item.fraction;23782379 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23802381 2382 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2383 .checked_sub(value)2384 .ok_or(Error::<T>::NumOverflow)?;2385 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23862387 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2388 .checked_add(value)2389 .ok_or(Error::<T>::NumOverflow)?;2390 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23912392 let old_owner = item.owner.clone();2393 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23942395 2396 if amount == value && !new_owner_has_account {2397 2398 2399 let mut new_full_item = full_item.clone();2400 new_full_item2401 .owner2402 .iter_mut()2403 .find(|i| i.owner == owner)2404 .expect("old owner does present in refungible")2405 .owner = new_owner.clone();2406 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24072408 2409 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2410 } else {2411 let mut new_full_item = full_item.clone();2412 new_full_item2413 .owner2414 .iter_mut()2415 .find(|i| i.owner == owner)2416 .expect("old owner does present in refungible")2417 .fraction -= value;24182419 2420 if new_owner_has_account {2421 2422 new_full_item2423 .owner2424 .iter_mut()2425 .find(|i| i.owner == new_owner)2426 .expect("new owner has account")2427 .fraction += value;2428 } else {2429 2430 new_full_item.owner.push(Ownership {2431 owner: new_owner.clone(),2432 fraction: value,2433 });2434 Self::add_token_index(collection_id, item_id, &new_owner)?;2435 }24362437 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2438 }24392440 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24412442 Ok(())2443 }24442445 fn transfer_nft(2446 collection: &CollectionHandle<T>,2447 item_id: TokenId,2448 sender: T::CrossAccountId,2449 new_owner: T::CrossAccountId,2450 ) -> DispatchResult {2451 let collection_id = collection.id;2452 let mut item = <NftItemList<T>>::get(collection_id, item_id)2453 .ok_or(Error::<T>::TokenNotFound)?;24542455 ensure!(2456 sender == item.owner,2457 Error::<T>::MustBeTokenOwner2458 );24592460 2461 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2462 .checked_sub(1)2463 .ok_or(Error::<T>::NumOverflow)?;2464 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24652466 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2467 .checked_add(1)2468 .ok_or(Error::<T>::NumOverflow)?;2469 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24702471 2472 let old_owner = item.owner.clone();2473 item.owner = new_owner.clone();2474 <NftItemList<T>>::insert(collection_id, item_id, item);24752476 2477 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24782479 collection.log(2480 Vec::from([2481 eth::TRANSFER_NFT_TOPIC,2482 eth::address_to_topic(sender.as_eth()),2483 eth::address_to_topic(new_owner.as_eth()),2484 eth::u32_to_topic(item_id),2485 ]),2486 abi_encode!(),2487 );2488 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24892490 Ok(())2491 }2492 2493 fn set_re_fungible_variable_data(2494 collection: &CollectionHandle<T>,2495 item_id: TokenId,2496 data: Vec<u8>2497 ) -> DispatchResult {2498 let collection_id = collection.id;2499 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2500 .ok_or(Error::<T>::TokenNotFound)?;25012502 item.variable_data = data;25032504 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25052506 Ok(())2507 }25082509 fn set_nft_variable_data(2510 collection: &CollectionHandle<T>,2511 item_id: TokenId,2512 data: Vec<u8>2513 ) -> DispatchResult {2514 let collection_id = collection.id;2515 let mut item = <NftItemList<T>>::get(collection_id, item_id)2516 .ok_or(Error::<T>::TokenNotFound)?;2517 2518 item.variable_data = data;25192520 <NftItemList<T>>::insert(collection_id, item_id, item);2521 2522 Ok(())2523 }25242525 fn init_collection(item: &Collection<T>) {2526 2527 assert!(2528 item.decimal_points <= MAX_DECIMAL_POINTS,2529 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2530 );2531 assert!(2532 item.name.len() <= 64,2533 "Collection name can not be longer than 63 char"2534 );2535 assert!(2536 item.name.len() <= 256,2537 "Collection description can not be longer than 255 char"2538 );2539 assert!(2540 item.token_prefix.len() <= 16,2541 "Token prefix can not be longer than 15 char"2542 );25432544 2545 let next_id = CreatedCollectionCount::get()2546 .checked_add(1)2547 .unwrap();25482549 CreatedCollectionCount::put(next_id);2550 }25512552 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2553 let current_index = <ItemListIndex>::get(collection_id)2554 .checked_add(1)2555 .unwrap();25562557 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25582559 <ItemListIndex>::insert(collection_id, current_index);25602561 2562 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2563 .checked_add(1)2564 .unwrap();2565 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2566 }25672568 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2569 let current_index = <ItemListIndex>::get(collection_id)2570 .checked_add(1)2571 .unwrap();25722573 Self::add_token_index(collection_id, current_index, owner).unwrap();25742575 <ItemListIndex>::insert(collection_id, current_index);25762577 2578 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2579 .checked_add(item.value)2580 .unwrap();2581 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2582 }25832584 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2585 let current_index = <ItemListIndex>::get(collection_id)2586 .checked_add(1)2587 .unwrap();25882589 let value = item.owner.first().unwrap().fraction;2590 let owner = item.owner.first().unwrap().owner.clone();25912592 Self::add_token_index(collection_id, current_index, &owner).unwrap();25932594 <ItemListIndex>::insert(collection_id, current_index);25952596 2597 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2598 .checked_add(value)2599 .unwrap();2600 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2601 }26022603 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2604 2605 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26062607 2608 let count = <AccountItemCount<T>>::get(owner.as_sub());2609 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26102611 <AccountItemCount<T>>::insert(owner.as_sub(), count2612 .checked_add(1)2613 .ok_or(Error::<T>::NumOverflow)?);2614 }2615 else {2616 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2617 }26182619 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2620 if list_exists {2621 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2622 let item_contains = list.contains(&item_index.clone());26232624 if !item_contains {2625 list.push(item_index.clone());2626 }26272628 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2629 } else {2630 let mut itm = Vec::new();2631 itm.push(item_index.clone());2632 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2633 }26342635 Ok(())2636 }26372638 fn remove_token_index(2639 collection_id: CollectionId,2640 item_index: TokenId,2641 owner: &T::CrossAccountId,2642 ) -> DispatchResult {26432644 2645 <AccountItemCount<T>>::insert(owner.as_sub(), 2646 <AccountItemCount<T>>::get(owner.as_sub())2647 .checked_sub(1)2648 .ok_or(Error::<T>::NumOverflow)?);264926502651 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2652 if list_exists {2653 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2654 let item_contains = list.contains(&item_index.clone());26552656 if item_contains {2657 list.retain(|&item| item != item_index);2658 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2659 }2660 }26612662 Ok(())2663 }26642665 fn move_token_index(2666 collection_id: CollectionId,2667 item_index: TokenId,2668 old_owner: &T::CrossAccountId,2669 new_owner: &T::CrossAccountId,2670 ) -> DispatchResult {2671 Self::remove_token_index(collection_id, item_index, old_owner)?;2672 Self::add_token_index(collection_id, item_index, new_owner)?;26732674 Ok(())2675 }2676 2677 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2678 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26792680 Ok(())2681 }2682}2683268426852686268726882689pub type Multiplier = FixedU128;26902691type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2692269326942695#[derive(Encode, Decode, Clone, Eq, PartialEq)]2696pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26972698impl<T: Config + Send + Sync> sp_std::fmt::Debug 2699 for ChargeTransactionPayment<T>2700{2701 #[cfg(feature = "std")]2702 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2703 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2704 }2705 #[cfg(not(feature = "std"))]2706 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2707 Ok(())2708 }2709}27102711impl<T: Config> ChargeTransactionPayment<T>2712where2713 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2714 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2715 T::AccountId: AsRef<[u8]>,2716 T::AccountId: UncheckedFrom<T::Hash>,2717{2718 fn traditional_fee(2719 len: usize,2720 info: &DispatchInfoOf<T::Call>,2721 tip: BalanceOf<T>,2722 ) -> BalanceOf<T>2723 where2724 T::Call: Dispatchable<Info = DispatchInfo>,2725 {2726 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2727 }27282729 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2730 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2731 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2732 let len_saturation = max_block_length as u64 / (len as u64).max(1);2733 let coefficient: BalanceOf<T> = weight_saturation2734 .min(len_saturation)2735 .saturated_into::<BalanceOf<T>>();2736 final_fee2737 .saturating_mul(coefficient)2738 .saturated_into::<TransactionPriority>()2739 }27402741 fn withdraw_fee(2742 &self,2743 who: &T::AccountId,2744 call: &T::Call,2745 info: &DispatchInfoOf<T::Call>,2746 len: usize,2747 ) -> Result<2748 (2749 BalanceOf<T>,2750 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2751 ),2752 TransactionValidityError,2753 > {2754 let tip = self.0;27552756 let fee = Self::traditional_fee(len, info, tip);27572758 2759 if fee.is_zero() {2760 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2761 .map(|i| (fee, i));2762 }27632764 2765 2766 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2767 Some(Call::create_item(collection_id, _owner, _properties)) => {2768 let collection = <CollectionById<T>>::get(collection_id)?;27692770 2771 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27722773 let limit = collection.limits.sponsor_transfer_timeout;2774 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2775 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2776 let limit_time = last_tx_block + limit.into();2777 if block_number <= limit_time {2778 return None;2779 }2780 }2781 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27822783 2784 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2785 collection.sponsorship.sponsor()2786 .cloned()2787 } else {2788 None2789 }2790 }2791 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2792 let collection = <CollectionById<T>>::get(collection_id)?;2793 2794 let mut sponsor_transfer = false;2795 if collection.sponsorship.confirmed() {27962797 let collection_limits = collection.limits;2798 let collection_mode = collection.mode;2799 2800 2801 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2802 sponsor_transfer = match collection_mode {2803 CollectionMode::NFT => {2804 2805 2806 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2807 collection_limits.sponsor_transfer_timeout2808 } else {2809 ChainLimit::get().nft_sponsor_transfer_timeout2810 };2811 2812 let mut sponsored = true;2813 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2814 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2815 let limit_time = last_tx_block + limit.into();2816 if block_number <= limit_time {2817 sponsored = false;2818 }2819 }2820 if sponsored {2821 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2822 }28232824 sponsored2825 }2826 CollectionMode::Fungible(_) => {2827 2828 2829 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2830 collection_limits.sponsor_transfer_timeout2831 } else {2832 ChainLimit::get().fungible_sponsor_transfer_timeout2833 };2834 2835 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2836 let mut sponsored = true;2837 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2838 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2839 let limit_time = last_tx_block + limit.into();2840 if block_number <= limit_time {2841 sponsored = false;2842 }2843 }2844 if sponsored {2845 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2846 }28472848 sponsored2849 }2850 CollectionMode::ReFungible => {2851 2852 2853 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2854 collection_limits.sponsor_transfer_timeout2855 } else {2856 ChainLimit::get().refungible_sponsor_transfer_timeout2857 };2858 2859 let mut sponsored = true;2860 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2861 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2862 let limit_time = last_tx_block + limit.into();2863 if block_number <= limit_time {2864 sponsored = false;2865 }2866 }2867 if sponsored {2868 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2869 }28702871 sponsored2872 }2873 _ => {2874 false2875 },2876 };2877 }28782879 if !sponsor_transfer {2880 None2881 } else {2882 collection.sponsorship.sponsor()2883 .cloned()2884 }2885 }28862887 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2888 let mut sponsor_metadata_changes = false;28892890 let collection = <CollectionById<T>>::get(collection_id)?;28912892 if2893 collection.sponsorship.confirmed() &&2894 2895 2896 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2897 data.len() <= collection.limits.sponsored_data_size as usize2898 {2899 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2900 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29012902 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2903 .map(|last_block| block_number - last_block > rate_limit)2904 .unwrap_or(true) 2905 {2906 sponsor_metadata_changes = true;2907 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2908 }2909 }2910 }29112912 if !sponsor_metadata_changes {2913 None2914 } else {2915 collection.sponsorship.sponsor().cloned()2916 }2917 }29182919 _ => None,2920 })();29212922 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2923 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29242925 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29262927 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2928 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2929 2930 if !owned_contract && white_list_enabled {2931 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2932 return Err(InvalidTransaction::Call.into());2933 }2934 }2935 },2936 _ => {},2937 }29382939 2940 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29412942 2943 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29442945 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2946 &who,2947 code_hash,2948 salt,2949 );2950 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29512952 None2953 },29542955 2956 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29572958 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2959 &who,2960 &T::Hashing::hash(&_code),2961 _salt,2962 );29632964 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29652966 None2967 }29682969 2970 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29712972 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29732974 let mut sponsor_transfer = false;2975 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2976 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2977 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2978 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2979 let limit_time = last_tx_block + rate_limit;29802981 if block_number >= limit_time {2982 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2983 sponsor_transfer = true;2984 }2985 } else {2986 sponsor_transfer = false;2987 }2988 2989 if sponsor_transfer {2990 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2991 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2992 return Some(called_contract);2993 }2994 }2995 }29962997 None2998 },29993000 _ => None,3001 });30023003 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30043005 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3006 .map(|i| (fee, i))3007 }3008}300930103011impl<T: Config + Send + Sync> SignedExtension3012 for ChargeTransactionPayment<T>3013where3014 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3015 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3016 T::AccountId: AsRef<[u8]>,3017 T::AccountId: UncheckedFrom<T::Hash>,3018{3019 const IDENTIFIER: &'static str = "ChargeTransactionPayment";3020 type AccountId = T::AccountId;3021 type Call = T::Call;3022 type AdditionalSigned = ();3023 type Pre = (3024 3025 BalanceOf<T>,3026 3027 Self::AccountId,3028 3029 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3030 );3031 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3032 Ok(())3033 }30343035 fn validate(3036 &self,3037 who: &Self::AccountId,3038 call: &Self::Call,3039 info: &DispatchInfoOf<Self::Call>,3040 len: usize,3041 ) -> TransactionValidity {3042 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3043 Ok(ValidTransaction {3044 priority: Self::get_priority(len, info, fee),3045 ..Default::default()3046 })3047 }30483049 fn pre_dispatch(3050 self,3051 who: &Self::AccountId,3052 call: &Self::Call,3053 info: &DispatchInfoOf<Self::Call>,3054 len: usize,3055 ) -> Result<Self::Pre, TransactionValidityError> {3056 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3057 Ok((self.0, who.clone(), imbalance))3058 }30593060 fn post_dispatch(3061 pre: Self::Pre,3062 info: &DispatchInfoOf<Self::Call>,3063 post_info: &PostDispatchInfoOf<Self::Call>,3064 len: usize,3065 _result: &DispatchResult,3066 ) -> Result<(), TransactionValidityError> {3067 let (tip, who, imbalance) = pre;3068 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3069 len as u32,3070 info,3071 post_info,3072 tip,3073 );3074 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3075 Ok(())3076 }3077}3078307930803081sp_api::decl_runtime_apis! {3082 pub trait NftApi {3083 3084 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3085 }3086}