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::CrossAccountId,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 CrossAccountId = <T as Config>::CrossAccountId,642 {643 644 645 646 647 648 649 650 651 652 CollectionCreated(CollectionId, u8, CrossAccountId),653654 655 656 657 658 659 660 661 662 663 ItemCreated(CollectionId, TokenId, CrossAccountId),664665 666 667 668 669 670 671 672 ItemDestroyed(CollectionId, TokenId),673674 675 676 677 678 679 680 681 682 683 684 685 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),686687 688 689 690 691 692 693 694 695 696 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),697 }698);699700decl_module! {701 pub struct Module<T: Config> for enum Call 702 where 703 origin: T::Origin704 {705 fn deposit_event() = default;706 type Error = Error<T>;707708 fn on_initialize(now: T::BlockNumber) -> Weight {709 0710 }711712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 #[weight = <T as Config>::WeightInfo::create_collection()]729 #[transactional]730 pub fn create_collection(origin,731 collection_name: Vec<u16>,732 collection_description: Vec<u16>,733 token_prefix: Vec<u8>,734 mode: CollectionMode) -> DispatchResult {735736 737 let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);738739 740 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();741 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(742 &T::TreasuryAccountId::get(),743 T::CollectionCreationPrice::get(),744 ));745 <T as Config>::Currency::settle(746 who.as_sub(),747 imbalance,748 WithdrawReasons::TRANSFER,749 ExistenceRequirement::KeepAlive,750 ).map_err(|_| Error::<T>::NoPermission)?;751752 let decimal_points = match mode {753 CollectionMode::Fungible(points) => points,754 _ => 0755 };756757 let chain_limit = ChainLimit::get();758759 let created_count = CreatedCollectionCount::get();760 let destroyed_count = DestroyedCollectionCount::get();761762 763 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);764765 766 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);767 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);768 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);769 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);770771 772 let next_id = created_count773 .checked_add(1)774 .ok_or(Error::<T>::NumOverflow)?;775776 CreatedCollectionCount::put(next_id);777778 let limits = CollectionLimits {779 sponsored_data_size: chain_limit.custom_data_limit,780 ..Default::default()781 };782783 784 let new_collection = Collection {785 owner: who.clone(),786 name: collection_name,787 mode: mode.clone(),788 mint_mode: false,789 access: AccessMode::Normal,790 description: collection_description,791 decimal_points: decimal_points,792 token_prefix: token_prefix,793 offchain_schema: Vec::new(),794 schema_version: SchemaVersion::ImageURL,795 sponsorship: SponsorshipState::Disabled,796 variable_on_chain_schema: Vec::new(),797 const_on_chain_schema: Vec::new(),798 limits,799 };800801 802 <CollectionById<T>>::insert(next_id, new_collection);803804 805 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));806807 Ok(())808 }809810 811 812 813 814 815 816 817 818 819 #[weight = <T as Config>::WeightInfo::destroy_collection()]820 #[transactional]821 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {822823 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);824 let collection = Self::get_collection(collection_id)?;825 Self::check_owner_permissions(&collection, &sender)?;826 if !collection.limits.owner_can_destroy {827 fail!(Error::<T>::NoPermission);828 }829830 <AddressTokens<T>>::remove_prefix(collection_id);831 <Allowances<T>>::remove_prefix(collection_id);832 <Balance<T>>::remove_prefix(collection_id);833 <ItemListIndex>::remove(collection_id);834 <AdminList<T>>::remove(collection_id);835 <CollectionById<T>>::remove(collection_id);836 <WhiteList<T>>::remove_prefix(collection_id);837838 <NftItemList<T>>::remove_prefix(collection_id);839 <FungibleItemList<T>>::remove_prefix(collection_id);840 <ReFungibleItemList<T>>::remove_prefix(collection_id);841842 <NftTransferBasket<T>>::remove_prefix(collection_id);843 <FungibleTransferBasket<T>>::remove_prefix(collection_id);844 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);845846 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);847848 DestroyedCollectionCount::put(DestroyedCollectionCount::get()849 .checked_add(1)850 .ok_or(Error::<T>::NumOverflow)?);851852 Ok(())853 }854855 856 857 858 859 860 861 862 863 864 865 866 867 #[weight = <T as Config>::WeightInfo::add_to_white_list()]868 #[transactional]869 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{870871 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);872 let collection = Self::get_collection(collection_id)?;873874 Self::toggle_white_list_internal(875 &sender,876 &collection,877 &address,878 true,879 )?;880881 Ok(())882 }883884 885 886 887 888 889 890 891 892 893 894 895 896 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]897 #[transactional]898 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{899900 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);901 let collection = Self::get_collection(collection_id)?;902903 Self::toggle_white_list_internal(904 &sender,905 &collection,906 &address,907 false,908 )?;909910 Ok(())911 }912913 914 915 916 917 918 919 920 921 922 923 924 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]925 #[transactional]926 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult927 {928 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);929930 let mut target_collection = Self::get_collection(collection_id)?;931 Self::check_owner_permissions(&target_collection, &sender)?;932 target_collection.access = mode;933 Self::save_collection(target_collection);934935 Ok(())936 }937938 939 940 941 942 943 944 945 946 947 948 949 950 951 #[weight = <T as Config>::WeightInfo::set_mint_permission()]952 #[transactional]953 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult954 {955 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);956957 let mut target_collection = Self::get_collection(collection_id)?;958 Self::check_owner_permissions(&target_collection, &sender)?;959 target_collection.mint_mode = mint_permission;960 Self::save_collection(target_collection);961962 Ok(())963 }964965 966 967 968 969 970 971 972 973 974 975 976 #[weight = <T as Config>::WeightInfo::change_collection_owner()]977 #[transactional]978 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {979980 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981 let mut target_collection = Self::get_collection(collection_id)?;982 Self::check_owner_permissions(&target_collection, &sender)?;983 target_collection.owner = new_owner;984 Self::save_collection(target_collection);985986 Ok(())987 }988989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 #[weight = <T as Config>::WeightInfo::add_collection_admin()]1003 #[transactional]1004 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1005 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006 let collection = Self::get_collection(collection_id)?;1007 Self::check_owner_or_admin_permissions(&collection, &sender)?;1008 let mut admin_arr = <AdminList<T>>::get(collection_id);10091010 match admin_arr.binary_search(&new_admin_id) {1011 Ok(_) => {},1012 Err(idx) => {1013 let limits = ChainLimit::get();1014 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1015 admin_arr.insert(idx, new_admin_id);1016 <AdminList<T>>::insert(collection_id, admin_arr);1017 }1018 }1019 Ok(())1020 }10211022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1035 #[transactional]1036 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1037 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038 let collection = Self::get_collection(collection_id)?;1039 Self::check_owner_or_admin_permissions(&collection, &sender)?;1040 let mut admin_arr = <AdminList<T>>::get(collection_id);10411042 match admin_arr.binary_search(&account_id) {1043 Ok(idx) => {1044 admin_arr.remove(idx);1045 <AdminList<T>>::insert(collection_id, admin_arr);1046 },1047 Err(_) => {}1048 }1049 Ok(())1050 }10511052 1053 1054 1055 1056 1057 1058 1059 1060 1061 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1062 #[transactional]1063 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1064 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1065 let mut target_collection = Self::get_collection(collection_id)?;1066 Self::check_owner_permissions(&target_collection, &sender)?;10671068 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1069 Self::save_collection(target_collection);10701071 Ok(())1072 }10731074 1075 1076 1077 1078 1079 1080 1081 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1082 #[transactional]1083 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1084 let sender = ensure_signed(origin)?;10851086 let mut target_collection = Self::get_collection(collection_id)?;1087 ensure!(1088 target_collection.sponsorship.pending_sponsor() == Some(&sender),1089 Error::<T>::ConfirmUnsetSponsorFail1090 );10911092 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1093 Self::save_collection(target_collection);10941095 Ok(())1096 }10971098 1099 1100 1101 1102 1103 1104 1105 1106 1107 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1108 #[transactional]1109 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1110 let sender = ensure_signed(origin)?;11111112 let mut target_collection = Self::get_collection(collection_id)?;1113 Self::check_owner_permissions(&target_collection, &T::CrossAccountId::from_sub(sender))?;11141115 target_collection.sponsorship = SponsorshipState::Disabled;1116 Self::save_collection(target_collection);11171118 Ok(())1119 }11201121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 11441145 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1146 #[transactional]1147 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1148 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1149 let collection = Self::get_collection(collection_id)?;11501151 Self::create_item_internal(&sender, &collection, &owner, data)?;11521153 Self::submit_logs(collection)?;1154 Ok(())1155 }11561157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1176 .map(|data| { data.len() })1177 .sum())]1178 #[transactional]1179 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11801181 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1182 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1183 let collection = Self::get_collection(collection_id)?;11841185 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11861187 Self::submit_logs(collection)?;1188 Ok(())1189 }11901191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 #[weight = <T as Config>::WeightInfo::burn_item()]1205 #[transactional]1206 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12071208 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1209 let target_collection = Self::get_collection(collection_id)?;12101211 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12121213 Self::submit_logs(target_collection)?;1214 Ok(())1215 }12161217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 #[weight = <T as Config>::WeightInfo::transfer()]1241 #[transactional]1242 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1243 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1244 let collection = Self::get_collection(collection_id)?;12451246 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12471248 Self::submit_logs(collection)?;1249 Ok(())1250 }12511252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 #[weight = <T as Config>::WeightInfo::approve()]1268 #[transactional]1269 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1270 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1271 let collection = Self::get_collection(collection_id)?;12721273 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12741275 Self::submit_logs(collection)?;1276 Ok(())1277 }1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 #[weight = <T as Config>::WeightInfo::transfer_from()]1299 #[transactional]1300 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1301 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1302 let collection = Self::get_collection(collection_id)?;13031304 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13051306 Self::submit_logs(collection)?;1307 Ok(())1308 }1309 1310 1311 1312 1313 13141315 13161317 13181319 1320 13211322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1335 #[transactional]1336 pub fn set_variable_meta_data (1337 origin,1338 collection_id: CollectionId,1339 item_id: TokenId,1340 data: Vec<u8>1341 ) -> DispatchResult {1342 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1343 1344 let collection = Self::get_collection(collection_id)?;13451346 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13471348 Ok(())1349 }1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 #[weight = <T as Config>::WeightInfo::set_schema_version()]1366 #[transactional]1367 pub fn set_schema_version(1368 origin,1369 collection_id: CollectionId,1370 version: SchemaVersion1371 ) -> DispatchResult {1372 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1373 let mut target_collection = Self::get_collection(collection_id)?;1374 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1375 target_collection.schema_version = version;1376 Self::save_collection(target_collection);13771378 Ok(())1379 }13801381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1394 #[transactional]1395 pub fn set_offchain_schema(1396 origin,1397 collection_id: CollectionId,1398 schema: Vec<u8>1399 ) -> DispatchResult {1400 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1401 let mut target_collection = Self::get_collection(collection_id)?;1402 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14031404 1405 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14061407 target_collection.offchain_schema = schema;1408 Self::save_collection(target_collection);14091410 Ok(())1411 }14121413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1426 #[transactional]1427 pub fn set_const_on_chain_schema (1428 origin,1429 collection_id: CollectionId,1430 schema: Vec<u8>1431 ) -> DispatchResult {1432 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1433 let mut target_collection = Self::get_collection(collection_id)?;1434 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14351436 1437 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14381439 target_collection.const_on_chain_schema = schema;1440 Self::save_collection(target_collection);14411442 Ok(())1443 }14441445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1458 #[transactional]1459 pub fn set_variable_on_chain_schema (1460 origin,1461 collection_id: CollectionId,1462 schema: Vec<u8>1463 ) -> DispatchResult {1464 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1465 let mut target_collection = Self::get_collection(collection_id)?;1466 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14671468 1469 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14701471 target_collection.variable_on_chain_schema = schema;1472 Self::save_collection(target_collection);14731474 Ok(())1475 }14761477 1478 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1479 #[transactional]1480 pub fn set_chain_limits(1481 origin,1482 limits: ChainLimits1483 ) -> DispatchResult {14841485 #[cfg(not(feature = "runtime-benchmarks"))]1486 ensure_root(origin)?;14871488 <ChainLimit>::put(limits);1489 Ok(())1490 }14911492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1504 #[transactional]1505 pub fn enable_contract_sponsoring(1506 origin,1507 contract_address: T::AccountId,1508 enable: bool1509 ) -> DispatchResult {15101511 let sender = ensure_signed(origin)?;15121513 #[cfg(feature = "runtime-benchmarks")]1514 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15151516 Self::ensure_contract_owned(sender, &contract_address)?;15171518 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1519 Ok(())1520 }15211522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1540 #[transactional]1541 pub fn set_contract_sponsoring_rate_limit(1542 origin,1543 contract_address: T::AccountId,1544 rate_limit: T::BlockNumber1545 ) -> DispatchResult {1546 let sender = ensure_signed(origin)?;15471548 #[cfg(feature = "runtime-benchmarks")]1549 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15501551 Self::ensure_contract_owned(sender, &contract_address)?;1552 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1553 Ok(())1554 }15551556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1568 #[transactional]1569 pub fn toggle_contract_white_list(1570 origin,1571 contract_address: T::AccountId,1572 enable: bool1573 ) -> DispatchResult {1574 let sender = ensure_signed(origin)?;15751576 #[cfg(feature = "runtime-benchmarks")]1577 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15781579 Self::ensure_contract_owned(sender, &contract_address)?;1580 if enable {1581 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1582 } else {1583 <ContractWhiteListEnabled<T>>::remove(contract_address);1584 }1585 Ok(())1586 }1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1600 #[transactional]1601 pub fn add_to_contract_white_list(1602 origin,1603 contract_address: T::AccountId,1604 account_address: T::AccountId1605 ) -> DispatchResult {1606 let sender = ensure_signed(origin)?;16071608 #[cfg(feature = "runtime-benchmarks")]1609 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1610 1611 Self::ensure_contract_owned(sender, &contract_address)?; 1612 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1613 Ok(())1614 }16151616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1628 #[transactional]1629 pub fn remove_from_contract_white_list(1630 origin,1631 contract_address: T::AccountId,1632 account_address: T::AccountId1633 ) -> DispatchResult {1634 let sender = ensure_signed(origin)?;16351636 #[cfg(feature = "runtime-benchmarks")]1637 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16381639 Self::ensure_contract_owned(sender, &contract_address)?;1640 <ContractWhiteList<T>>::remove(contract_address, account_address);1641 Ok(())1642 }16431644 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1645 #[transactional]1646 pub fn set_collection_limits(1647 origin,1648 collection_id: u32,1649 new_limits: CollectionLimits<T::BlockNumber>,1650 ) -> DispatchResult {1651 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1652 let mut target_collection = Self::get_collection(collection_id)?;1653 Self::check_owner_permissions(&target_collection, &sender)?;1654 let old_limits = &target_collection.limits;1655 let chain_limits = ChainLimit::get();16561657 1658 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1659 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1660 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1661 Error::<T>::CollectionLimitBoundsExceeded);16621663 1664 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1665 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16661667 ensure!(1668 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1669 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1670 Error::<T>::OwnerPermissionsCantBeReverted,1671 );16721673 target_collection.limits = new_limits;1674 Self::save_collection(target_collection);16751676 Ok(())1677 } 1678 }1679}16801681impl<T: Config> Module<T> {1682 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1683 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1684 Self::validate_create_item_args(&collection, &data)?;1685 Self::create_item_no_validation(&collection, owner, data)?;16861687 Ok(())1688 }16891690 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1691 1692 Self::is_correct_transfer(target_collection, &recipient)?;16931694 1695 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1696 Self::is_owner_or_admin_permissions(target_collection, &sender),1697 Error::<T>::NoPermission);16981699 if target_collection.access == AccessMode::WhiteList {1700 Self::check_white_list(target_collection, &sender)?;1701 Self::check_white_list(target_collection, &recipient)?;1702 }17031704 match target_collection.mode1705 {1706 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1707 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1708 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1709 _ => ()1710 };17111712 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17131714 Ok(())1715 }17161717 pub fn approve_internal(1718 sender: &T::CrossAccountId,1719 spender: &T::CrossAccountId,1720 collection: &CollectionHandle<T>,1721 item_id: TokenId,1722 amount: u1281723 ) -> DispatchResult {1724 Self::token_exists(&collection, item_id)?;17251726 1727 let bypasses_limits = collection.limits.owner_can_transfer &&1728 Self::is_owner_or_admin_permissions(1729 &collection,1730 &sender,1731 );17321733 let allowance_limit = if bypasses_limits {1734 None1735 } else if let Some(amount) = Self::owned_amount(1736 &sender,1737 &collection,1738 item_id,1739 ) {1740 Some(amount)1741 } else {1742 fail!(Error::<T>::NoPermission);1743 };17441745 if collection.access == AccessMode::WhiteList {1746 Self::check_white_list(&collection, &sender)?;1747 Self::check_white_list(&collection, &spender)?;1748 }17491750 let allowance: u128 = amount1751 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1752 .ok_or(Error::<T>::NumOverflow)?;1753 if let Some(limit) = allowance_limit {1754 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1755 }1756 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17571758 if matches!(collection.mode, CollectionMode::NFT) {1759 1760 collection.log(1761 Vec::from([1762 eth::APPROVAL_NFT_TOPIC,1763 eth::address_to_topic(sender.as_eth()),1764 eth::address_to_topic(spender.as_eth()),1765 eth::u32_to_topic(item_id),1766 ]),1767 abi_encode!(),1768 );1769 }17701771 if matches!(collection.mode, CollectionMode::Fungible(_)) {1772 1773 collection.log(1774 Vec::from([1775 eth::APPROVAL_FUNGIBLE_TOPIC,1776 eth::address_to_topic(sender.as_eth()),1777 eth::address_to_topic(spender.as_eth()),1778 ]),1779 abi_encode!(uint256(allowance.into())),1780 );1781 }17821783 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1784 Ok(())1785 }17861787 pub fn transfer_from_internal(1788 sender: &T::CrossAccountId,1789 from: &T::CrossAccountId,1790 recipient: &T::CrossAccountId,1791 collection: &CollectionHandle<T>,1792 item_id: TokenId,1793 amount: u128,1794 ) -> DispatchResult {1795 1796 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17971798 1799 Self::is_correct_transfer(&collection, &recipient)?;18001801 1802 ensure!(1803 approval >= amount || 1804 (1805 collection.limits.owner_can_transfer &&1806 Self::is_owner_or_admin_permissions(&collection, &sender)1807 ),1808 Error::<T>::NoPermission1809 );18101811 if collection.access == AccessMode::WhiteList {1812 Self::check_white_list(&collection, &sender)?;1813 Self::check_white_list(&collection, &recipient)?;1814 }18151816 1817 let allowance = approval.saturating_sub(amount);1818 if allowance > 0 {1819 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1820 } else {1821 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1822 }18231824 match collection.mode {1825 CollectionMode::NFT => {1826 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1827 }1828 CollectionMode::Fungible(_) => {1829 Self::transfer_fungible(&collection, amount, &from, &recipient)?1830 }1831 CollectionMode::ReFungible => {1832 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1833 }1834 _ => ()1835 };18361837 if matches!(collection.mode, CollectionMode::Fungible(_)) {1838 collection.log(1839 Vec::from([1840 eth::APPROVAL_FUNGIBLE_TOPIC,1841 eth::address_to_topic(from.as_eth()),1842 eth::address_to_topic(sender.as_eth()),1843 ]),1844 abi_encode!(uint256(allowance.into())),1845 );1846 }18471848 Ok(())1849 }18501851 pub fn set_variable_meta_data_internal(1852 sender: &T::CrossAccountId,1853 collection: &CollectionHandle<T>, 1854 item_id: TokenId,1855 data: Vec<u8>,1856 ) -> DispatchResult {1857 Self::token_exists(&collection, item_id)?;18581859 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18601861 1862 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1863 Self::is_owner_or_admin_permissions(&collection, &sender),1864 Error::<T>::NoPermission);18651866 match collection.mode1867 {1868 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1869 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1870 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1871 _ => fail!(Error::<T>::UnexpectedCollectionType)1872 };18731874 Ok(())1875 }18761877 pub fn create_multiple_items_internal(1878 sender: &T::CrossAccountId,1879 collection: &CollectionHandle<T>,1880 owner: &T::CrossAccountId,1881 items_data: Vec<CreateItemData>,1882 ) -> DispatchResult {1883 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18841885 for data in &items_data {1886 Self::validate_create_item_args(&collection, data)?;1887 }1888 for data in &items_data {1889 Self::create_item_no_validation(&collection, owner, data.clone())?;1890 }18911892 Ok(())1893 }18941895 pub fn burn_item_internal(1896 sender: &T::CrossAccountId,1897 collection: &CollectionHandle<T>,1898 item_id: TokenId,1899 value: u128,1900 ) -> DispatchResult {1901 ensure!(1902 Self::is_item_owner(&sender, &collection, item_id) ||1903 (1904 collection.limits.owner_can_transfer &&1905 Self::is_owner_or_admin_permissions(&collection, &sender)1906 ),1907 Error::<T>::NoPermission1908 );19091910 if collection.access == AccessMode::WhiteList {1911 Self::check_white_list(&collection, &sender)?;1912 }19131914 match collection.mode1915 {1916 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1917 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1918 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1919 _ => ()1920 };19211922 Ok(())1923 }19241925 pub fn toggle_white_list_internal(1926 sender: &T::CrossAccountId,1927 collection: &CollectionHandle<T>,1928 address: &T::CrossAccountId,1929 whitelisted: bool,1930 ) -> DispatchResult {1931 Self::check_owner_or_admin_permissions(&collection, &sender)?;19321933 if whitelisted {1934 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1935 } else {1936 <WhiteList<T>>::remove(collection.id, address.as_sub());1937 }19381939 Ok(())1940 }19411942 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1943 let collection_id = collection.id;19441945 1946 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1947 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1948 1949 Ok(())1950 }19511952 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1953 let collection_id = collection.id;19541955 1956 let total_items: u32 = ItemListIndex::get(collection_id)1957 .checked_add(amount)1958 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1959 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1960 .checked_add(amount)1961 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1962 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1963 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19641965 if !Self::is_owner_or_admin_permissions(collection, &sender) {1966 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1967 Self::check_white_list(collection, owner)?;1968 Self::check_white_list(collection, sender)?;1969 }19701971 Ok(())1972 }19731974 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1975 match target_collection.mode1976 {1977 CollectionMode::NFT => {1978 if let CreateItemData::NFT(data) = data {1979 1980 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1981 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1982 } else {1983 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1984 }1985 },1986 CollectionMode::Fungible(_) => {1987 if let CreateItemData::Fungible(_) = data {1988 } else {1989 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1990 }1991 },1992 CollectionMode::ReFungible => {1993 if let CreateItemData::ReFungible(data) = data {19941995 1996 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1997 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19981999 2000 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2001 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2002 } else {2003 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2004 }2005 },2006 _ => { fail!(Error::<T>::UnexpectedCollectionType); }2007 };20082009 Ok(())2010 }20112012 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2013 match data2014 {2015 CreateItemData::NFT(data) => {2016 let item = NftItemType {2017 owner: owner.clone(),2018 const_data: data.const_data,2019 variable_data: data.variable_data2020 };20212022 Self::add_nft_item(collection, item)?;2023 },2024 CreateItemData::Fungible(data) => {2025 Self::add_fungible_item(collection, &owner, data.value)?;2026 },2027 CreateItemData::ReFungible(data) => {2028 let mut owner_list = Vec::new();2029 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20302031 let item = ReFungibleItemType {2032 owner: owner_list,2033 const_data: data.const_data,2034 variable_data: data.variable_data2035 };20362037 Self::add_refungible_item(collection, item)?;2038 }2039 };20402041 Ok(())2042 }20432044 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2045 let collection_id = collection.id;20462047 2048 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20492050 2051 let item = FungibleItemType {2052 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2053 };2054 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20552056 2057 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2058 .checked_add(value)2059 .ok_or(Error::<T>::NumOverflow)?;2060 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20612062 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2063 Ok(())2064 }20652066 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2067 let collection_id = collection.id;20682069 let current_index = <ItemListIndex>::get(collection_id)2070 .checked_add(1)2071 .ok_or(Error::<T>::NumOverflow)?;2072 let itemcopy = item.clone();20732074 ensure!(2075 item.owner.len() == 1,2076 Error::<T>::BadCreateRefungibleCall,2077 );2078 let item_owner = item.owner.first().expect("only one owner is defined");20792080 let value = item_owner.fraction;2081 let owner = item_owner.owner.clone();20822083 Self::add_token_index(collection_id, current_index, &owner)?;20842085 <ItemListIndex>::insert(collection_id, current_index);2086 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20872088 2089 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2090 .checked_add(value)2091 .ok_or(Error::<T>::NumOverflow)?;2092 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20932094 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2095 Ok(())2096 }20972098 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2099 let collection_id = collection.id;21002101 let current_index = <ItemListIndex>::get(collection_id)2102 .checked_add(1)2103 .ok_or(Error::<T>::NumOverflow)?;21042105 let item_owner = item.owner.clone();2106 Self::add_token_index(collection_id, current_index, &item.owner)?;21072108 <ItemListIndex>::insert(collection_id, current_index);2109 <NftItemList<T>>::insert(collection_id, current_index, item);21102111 2112 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2113 .checked_add(1)2114 .ok_or(Error::<T>::NumOverflow)?;2115 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21162117 collection.log(2118 Vec::from([2119 eth::TRANSFER_NFT_TOPIC,2120 eth::address_to_topic(&H160::default()),2121 eth::address_to_topic(item_owner.as_eth()),2122 eth::u32_to_topic(current_index),2123 ]),2124 abi_encode!(),2125 );2126 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2127 Ok(())2128 }21292130 fn burn_refungible_item(2131 collection: &CollectionHandle<T>,2132 item_id: TokenId,2133 owner: &T::CrossAccountId,2134 ) -> DispatchResult {2135 let collection_id = collection.id;21362137 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2138 .ok_or(Error::<T>::TokenNotFound)?;2139 let rft_balance = token2140 .owner2141 .iter()2142 .find(|&i| i.owner == *owner)2143 .ok_or(Error::<T>::TokenNotFound)?;2144 Self::remove_token_index(collection_id, item_id, owner)?;21452146 2147 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2148 .checked_sub(rft_balance.fraction)2149 .ok_or(Error::<T>::NumOverflow)?;2150 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21512152 2153 let index = token2154 .owner2155 .iter()2156 .position(|i| i.owner == *owner)2157 .expect("owned item is exists");2158 token.owner.remove(index);2159 let owner_count = token.owner.len();21602161 2162 if owner_count == 0 {2163 <ReFungibleItemList<T>>::remove(collection_id, item_id);2164 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2165 }2166 else {2167 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2168 }21692170 Ok(())2171 }21722173 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2174 let collection_id = collection.id;21752176 let item = <NftItemList<T>>::get(collection_id, item_id)2177 .ok_or(Error::<T>::TokenNotFound)?;2178 Self::remove_token_index(collection_id, item_id, &item.owner)?;21792180 2181 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2182 .checked_sub(1)2183 .ok_or(Error::<T>::NumOverflow)?;2184 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2185 <NftItemList<T>>::remove(collection_id, item_id);2186 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21872188 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2189 Ok(())2190 }21912192 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2193 let collection_id = collection.id;21942195 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2196 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21972198 2199 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2200 .checked_sub(value)2201 .ok_or(Error::<T>::NumOverflow)?;2202 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22032204 if balance.value - value > 0 {2205 balance.value -= value;2206 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2207 }2208 else {2209 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2210 }22112212 collection.log(2213 Vec::from([2214 eth::TRANSFER_FUNGIBLE_TOPIC,2215 eth::address_to_topic(owner.as_eth()),2216 eth::address_to_topic(&H160::default()),2217 ]),2218 abi_encode!(uint256(value.into())),2219 );2220 Ok(())2221 }22222223 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2224 Ok(<CollectionHandle<T>>::get(collection_id)2225 .ok_or(Error::<T>::CollectionNotFound)?)2226 }22272228 fn save_collection(collection: CollectionHandle<T>) {2229 <CollectionById<T>>::insert(collection.id, collection.into_inner());2230 }22312232 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2233 if collection.logs.is_empty() {2234 return Ok(())2235 }2236 T::EthereumTransactionSender::submit_logs_transaction(2237 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2238 collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2239 )2240 }22412242 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2243 ensure!(2244 *subject == target_collection.owner,2245 Error::<T>::NoPermission2246 );22472248 Ok(())2249 }22502251 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2252 *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2253 }22542255 fn check_owner_or_admin_permissions(2256 collection: &CollectionHandle<T>,2257 subject: &T::CrossAccountId,2258 ) -> DispatchResult {2259 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22602261 Ok(())2262 }22632264 fn owned_amount(2265 subject: &T::CrossAccountId,2266 target_collection: &CollectionHandle<T>,2267 item_id: TokenId,2268 ) -> Option<u128> {2269 let collection_id = target_collection.id;22702271 match target_collection.mode {2272 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2273 .then(|| 1),2274 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2275 .value),2276 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2277 .owner2278 .iter()2279 .find(|i| i.owner == *subject)2280 .map(|i| i.fraction),2281 CollectionMode::Invalid => None,2282 }2283 }22842285 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2286 match target_collection.mode {2287 CollectionMode::Fungible(_) => true,2288 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2289 }2290 }22912292 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2293 let collection_id = collection.id;22942295 let mes = Error::<T>::AddresNotInWhiteList;2296 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22972298 Ok(())2299 }23002301 2302 2303 fn token_exists(2304 target_collection: &CollectionHandle<T>,2305 item_id: TokenId,2306 ) -> DispatchResult {2307 let collection_id = target_collection.id;2308 let exists = match target_collection.mode2309 {2310 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2311 CollectionMode::Fungible(_) => true,2312 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2313 _ => false2314 };23152316 ensure!(exists == true, Error::<T>::TokenNotFound);2317 Ok(())2318 }23192320 fn transfer_fungible(2321 collection: &CollectionHandle<T>,2322 value: u128,2323 owner: &T::CrossAccountId,2324 recipient: &T::CrossAccountId,2325 ) -> DispatchResult {2326 let collection_id = collection.id;23272328 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2329 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23302331 2332 Self::add_fungible_item(collection, recipient, value)?;23332334 2335 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23362337 2338 if balance.value == value {2339 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2340 }2341 else {2342 balance.value -= value;2343 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2344 }23452346 collection.log(2347 Vec::from([2348 eth::TRANSFER_FUNGIBLE_TOPIC,2349 eth::address_to_topic(owner.as_eth()),2350 eth::address_to_topic(recipient.as_eth()),2351 ]),2352 abi_encode!(uint256(value.into())),2353 );2354 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23552356 Ok(())2357 }23582359 fn transfer_refungible(2360 collection: &CollectionHandle<T>,2361 item_id: TokenId,2362 value: u128,2363 owner: T::CrossAccountId,2364 new_owner: T::CrossAccountId,2365 ) -> DispatchResult {2366 let collection_id = collection.id;2367 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2368 .ok_or(Error::<T>::TokenNotFound)?;23692370 let item = full_item2371 .owner2372 .iter()2373 .filter(|i| i.owner == owner)2374 .next()2375 .ok_or(Error::<T>::TokenNotFound)?;2376 let amount = item.fraction;23772378 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23792380 2381 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2382 .checked_sub(value)2383 .ok_or(Error::<T>::NumOverflow)?;2384 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23852386 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2387 .checked_add(value)2388 .ok_or(Error::<T>::NumOverflow)?;2389 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23902391 let old_owner = item.owner.clone();2392 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23932394 2395 if amount == value && !new_owner_has_account {2396 2397 2398 let mut new_full_item = full_item.clone();2399 new_full_item2400 .owner2401 .iter_mut()2402 .find(|i| i.owner == owner)2403 .expect("old owner does present in refungible")2404 .owner = new_owner.clone();2405 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24062407 2408 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2409 } else {2410 let mut new_full_item = full_item.clone();2411 new_full_item2412 .owner2413 .iter_mut()2414 .find(|i| i.owner == owner)2415 .expect("old owner does present in refungible")2416 .fraction -= value;24172418 2419 if new_owner_has_account {2420 2421 new_full_item2422 .owner2423 .iter_mut()2424 .find(|i| i.owner == new_owner)2425 .expect("new owner has account")2426 .fraction += value;2427 } else {2428 2429 new_full_item.owner.push(Ownership {2430 owner: new_owner.clone(),2431 fraction: value,2432 });2433 Self::add_token_index(collection_id, item_id, &new_owner)?;2434 }24352436 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2437 }24382439 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24402441 Ok(())2442 }24432444 fn transfer_nft(2445 collection: &CollectionHandle<T>,2446 item_id: TokenId,2447 sender: T::CrossAccountId,2448 new_owner: T::CrossAccountId,2449 ) -> DispatchResult {2450 let collection_id = collection.id;2451 let mut item = <NftItemList<T>>::get(collection_id, item_id)2452 .ok_or(Error::<T>::TokenNotFound)?;24532454 ensure!(2455 sender == item.owner,2456 Error::<T>::MustBeTokenOwner2457 );24582459 2460 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2461 .checked_sub(1)2462 .ok_or(Error::<T>::NumOverflow)?;2463 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24642465 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2466 .checked_add(1)2467 .ok_or(Error::<T>::NumOverflow)?;2468 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24692470 2471 let old_owner = item.owner.clone();2472 item.owner = new_owner.clone();2473 <NftItemList<T>>::insert(collection_id, item_id, item);24742475 2476 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24772478 collection.log(2479 Vec::from([2480 eth::TRANSFER_NFT_TOPIC,2481 eth::address_to_topic(sender.as_eth()),2482 eth::address_to_topic(new_owner.as_eth()),2483 eth::u32_to_topic(item_id),2484 ]),2485 abi_encode!(),2486 );2487 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24882489 Ok(())2490 }2491 2492 fn set_re_fungible_variable_data(2493 collection: &CollectionHandle<T>,2494 item_id: TokenId,2495 data: Vec<u8>2496 ) -> DispatchResult {2497 let collection_id = collection.id;2498 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2499 .ok_or(Error::<T>::TokenNotFound)?;25002501 item.variable_data = data;25022503 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25042505 Ok(())2506 }25072508 fn set_nft_variable_data(2509 collection: &CollectionHandle<T>,2510 item_id: TokenId,2511 data: Vec<u8>2512 ) -> DispatchResult {2513 let collection_id = collection.id;2514 let mut item = <NftItemList<T>>::get(collection_id, item_id)2515 .ok_or(Error::<T>::TokenNotFound)?;2516 2517 item.variable_data = data;25182519 <NftItemList<T>>::insert(collection_id, item_id, item);2520 2521 Ok(())2522 }25232524 fn init_collection(item: &Collection<T>) {2525 2526 assert!(2527 item.decimal_points <= MAX_DECIMAL_POINTS,2528 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2529 );2530 assert!(2531 item.name.len() <= 64,2532 "Collection name can not be longer than 63 char"2533 );2534 assert!(2535 item.name.len() <= 256,2536 "Collection description can not be longer than 255 char"2537 );2538 assert!(2539 item.token_prefix.len() <= 16,2540 "Token prefix can not be longer than 15 char"2541 );25422543 2544 let next_id = CreatedCollectionCount::get()2545 .checked_add(1)2546 .unwrap();25472548 CreatedCollectionCount::put(next_id);2549 }25502551 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2552 let current_index = <ItemListIndex>::get(collection_id)2553 .checked_add(1)2554 .unwrap();25552556 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25572558 <ItemListIndex>::insert(collection_id, current_index);25592560 2561 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2562 .checked_add(1)2563 .unwrap();2564 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2565 }25662567 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2568 let current_index = <ItemListIndex>::get(collection_id)2569 .checked_add(1)2570 .unwrap();25712572 Self::add_token_index(collection_id, current_index, owner).unwrap();25732574 <ItemListIndex>::insert(collection_id, current_index);25752576 2577 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2578 .checked_add(item.value)2579 .unwrap();2580 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2581 }25822583 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2584 let current_index = <ItemListIndex>::get(collection_id)2585 .checked_add(1)2586 .unwrap();25872588 let value = item.owner.first().unwrap().fraction;2589 let owner = item.owner.first().unwrap().owner.clone();25902591 Self::add_token_index(collection_id, current_index, &owner).unwrap();25922593 <ItemListIndex>::insert(collection_id, current_index);25942595 2596 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2597 .checked_add(value)2598 .unwrap();2599 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2600 }26012602 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2603 2604 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26052606 2607 let count = <AccountItemCount<T>>::get(owner.as_sub());2608 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26092610 <AccountItemCount<T>>::insert(owner.as_sub(), count2611 .checked_add(1)2612 .ok_or(Error::<T>::NumOverflow)?);2613 }2614 else {2615 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2616 }26172618 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2619 if list_exists {2620 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2621 let item_contains = list.contains(&item_index.clone());26222623 if !item_contains {2624 list.push(item_index.clone());2625 }26262627 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2628 } else {2629 let mut itm = Vec::new();2630 itm.push(item_index.clone());2631 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2632 }26332634 Ok(())2635 }26362637 fn remove_token_index(2638 collection_id: CollectionId,2639 item_index: TokenId,2640 owner: &T::CrossAccountId,2641 ) -> DispatchResult {26422643 2644 <AccountItemCount<T>>::insert(owner.as_sub(), 2645 <AccountItemCount<T>>::get(owner.as_sub())2646 .checked_sub(1)2647 .ok_or(Error::<T>::NumOverflow)?);264826492650 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2651 if list_exists {2652 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2653 let item_contains = list.contains(&item_index.clone());26542655 if item_contains {2656 list.retain(|&item| item != item_index);2657 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2658 }2659 }26602661 Ok(())2662 }26632664 fn move_token_index(2665 collection_id: CollectionId,2666 item_index: TokenId,2667 old_owner: &T::CrossAccountId,2668 new_owner: &T::CrossAccountId,2669 ) -> DispatchResult {2670 Self::remove_token_index(collection_id, item_index, old_owner)?;2671 Self::add_token_index(collection_id, item_index, new_owner)?;26722673 Ok(())2674 }2675 2676 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2677 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26782679 Ok(())2680 }2681}2682268326842685268626872688pub type Multiplier = FixedU128;26892690type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2691269226932694#[derive(Encode, Decode, Clone, Eq, PartialEq)]2695pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26962697impl<T: Config + Send + Sync> sp_std::fmt::Debug 2698 for ChargeTransactionPayment<T>2699{2700 #[cfg(feature = "std")]2701 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2702 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2703 }2704 #[cfg(not(feature = "std"))]2705 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2706 Ok(())2707 }2708}27092710impl<T: Config> ChargeTransactionPayment<T>2711where2712 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2713 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2714 T::AccountId: AsRef<[u8]>,2715 T::AccountId: UncheckedFrom<T::Hash>,2716{2717 fn traditional_fee(2718 len: usize,2719 info: &DispatchInfoOf<T::Call>,2720 tip: BalanceOf<T>,2721 ) -> BalanceOf<T>2722 where2723 T::Call: Dispatchable<Info = DispatchInfo>,2724 {2725 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2726 }27272728 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2729 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2730 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2731 let len_saturation = max_block_length as u64 / (len as u64).max(1);2732 let coefficient: BalanceOf<T> = weight_saturation2733 .min(len_saturation)2734 .saturated_into::<BalanceOf<T>>();2735 final_fee2736 .saturating_mul(coefficient)2737 .saturated_into::<TransactionPriority>()2738 }27392740 fn withdraw_fee(2741 &self,2742 who: &T::AccountId,2743 call: &T::Call,2744 info: &DispatchInfoOf<T::Call>,2745 len: usize,2746 ) -> Result<2747 (2748 BalanceOf<T>,2749 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2750 ),2751 TransactionValidityError,2752 > {2753 let tip = self.0;27542755 let fee = Self::traditional_fee(len, info, tip);27562757 2758 if fee.is_zero() {2759 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2760 .map(|i| (fee, i));2761 }27622763 2764 2765 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2766 Some(Call::create_item(collection_id, _owner, _properties)) => {2767 let collection = <CollectionById<T>>::get(collection_id)?;27682769 2770 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27712772 let limit = collection.limits.sponsor_transfer_timeout;2773 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2774 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2775 let limit_time = last_tx_block + limit.into();2776 if block_number <= limit_time {2777 return None;2778 }2779 }2780 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27812782 2783 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2784 collection.sponsorship.sponsor()2785 .cloned()2786 } else {2787 None2788 }2789 }2790 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2791 let collection = <CollectionById<T>>::get(collection_id)?;2792 2793 let mut sponsor_transfer = false;2794 if collection.sponsorship.confirmed() {27952796 let collection_limits = collection.limits;2797 let collection_mode = collection.mode;2798 2799 2800 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2801 sponsor_transfer = match collection_mode {2802 CollectionMode::NFT => {2803 2804 2805 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2806 collection_limits.sponsor_transfer_timeout2807 } else {2808 ChainLimit::get().nft_sponsor_transfer_timeout2809 };2810 2811 let mut sponsored = true;2812 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2813 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2814 let limit_time = last_tx_block + limit.into();2815 if block_number <= limit_time {2816 sponsored = false;2817 }2818 }2819 if sponsored {2820 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2821 }28222823 sponsored2824 }2825 CollectionMode::Fungible(_) => {2826 2827 2828 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2829 collection_limits.sponsor_transfer_timeout2830 } else {2831 ChainLimit::get().fungible_sponsor_transfer_timeout2832 };2833 2834 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2835 let mut sponsored = true;2836 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2837 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2838 let limit_time = last_tx_block + limit.into();2839 if block_number <= limit_time {2840 sponsored = false;2841 }2842 }2843 if sponsored {2844 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2845 }28462847 sponsored2848 }2849 CollectionMode::ReFungible => {2850 2851 2852 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2853 collection_limits.sponsor_transfer_timeout2854 } else {2855 ChainLimit::get().refungible_sponsor_transfer_timeout2856 };2857 2858 let mut sponsored = true;2859 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2860 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2861 let limit_time = last_tx_block + limit.into();2862 if block_number <= limit_time {2863 sponsored = false;2864 }2865 }2866 if sponsored {2867 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2868 }28692870 sponsored2871 }2872 _ => {2873 false2874 },2875 };2876 }28772878 if !sponsor_transfer {2879 None2880 } else {2881 collection.sponsorship.sponsor()2882 .cloned()2883 }2884 }28852886 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2887 let mut sponsor_metadata_changes = false;28882889 let collection = <CollectionById<T>>::get(collection_id)?;28902891 if2892 collection.sponsorship.confirmed() &&2893 2894 2895 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2896 data.len() <= collection.limits.sponsored_data_size as usize2897 {2898 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2899 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29002901 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2902 .map(|last_block| block_number - last_block > rate_limit)2903 .unwrap_or(true) 2904 {2905 sponsor_metadata_changes = true;2906 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2907 }2908 }2909 }29102911 if !sponsor_metadata_changes {2912 None2913 } else {2914 collection.sponsorship.sponsor().cloned()2915 }2916 }29172918 _ => None,2919 })();29202921 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2922 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29232924 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29252926 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2927 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2928 2929 if !owned_contract && white_list_enabled {2930 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2931 return Err(InvalidTransaction::Call.into());2932 }2933 }2934 },2935 _ => {},2936 }29372938 2939 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29402941 2942 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29432944 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2945 &who,2946 code_hash,2947 salt,2948 );2949 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29502951 None2952 },29532954 2955 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29562957 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2958 &who,2959 &T::Hashing::hash(&_code),2960 _salt,2961 );29622963 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29642965 None2966 }29672968 2969 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29702971 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29722973 let mut sponsor_transfer = false;2974 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2975 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2976 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2977 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2978 let limit_time = last_tx_block + rate_limit;29792980 if block_number >= limit_time {2981 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2982 sponsor_transfer = true;2983 }2984 } else {2985 sponsor_transfer = false;2986 }2987 2988 if sponsor_transfer {2989 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2990 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2991 return Some(called_contract);2992 }2993 }2994 }29952996 None2997 },29982999 _ => None,3000 });30013002 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30033004 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3005 .map(|i| (fee, i))3006 }3007}300830093010impl<T: Config + Send + Sync> SignedExtension3011 for ChargeTransactionPayment<T>3012where3013 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3014 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3015 T::AccountId: AsRef<[u8]>,3016 T::AccountId: UncheckedFrom<T::Hash>,3017{3018 const IDENTIFIER: &'static str = "ChargeTransactionPayment";3019 type AccountId = T::AccountId;3020 type Call = T::Call;3021 type AdditionalSigned = ();3022 type Pre = (3023 3024 BalanceOf<T>,3025 3026 Self::AccountId,3027 3028 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3029 );3030 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3031 Ok(())3032 }30333034 fn validate(3035 &self,3036 who: &Self::AccountId,3037 call: &Self::Call,3038 info: &DispatchInfoOf<Self::Call>,3039 len: usize,3040 ) -> TransactionValidity {3041 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3042 Ok(ValidTransaction {3043 priority: Self::get_priority(len, info, fee),3044 ..Default::default()3045 })3046 }30473048 fn pre_dispatch(3049 self,3050 who: &Self::AccountId,3051 call: &Self::Call,3052 info: &DispatchInfoOf<Self::Call>,3053 len: usize,3054 ) -> Result<Self::Pre, TransactionValidityError> {3055 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3056 Ok((self.0, who.clone(), imbalance))3057 }30583059 fn post_dispatch(3060 pre: Self::Pre,3061 info: &DispatchInfoOf<Self::Call>,3062 post_info: &PostDispatchInfoOf<Self::Call>,3063 len: usize,3064 _result: &DispatchResult,3065 ) -> Result<(), TransactionValidityError> {3066 let (tip, who, imbalance) = pre;3067 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3068 len as u32,3069 info,3070 post_info,3071 tip,3072 );3073 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3074 Ok(())3075 }3076}3077307830793080sp_api::decl_runtime_apis! {3081 pub trait NftApi {3082 3083 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3084 }3085}