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::account::*;5960pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;61pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;62pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;63pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6465666768pub type CollectionId = u32;69pub type TokenId = u32;70pub type DecimalPoints = u8;7172#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]73#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]74pub enum CollectionMode {75 Invalid,76 NFT,77 78 Fungible(DecimalPoints),79 ReFungible,80}8182impl Default for CollectionMode {83 fn default() -> Self {84 Self::Invalid85 }86}8788impl Into<u8> for CollectionMode {89 fn into(self) -> u8 {90 match self {91 CollectionMode::Invalid => 0,92 CollectionMode::NFT => 1,93 CollectionMode::Fungible(_) => 2,94 CollectionMode::ReFungible => 3,95 }96 }97}9899#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub enum AccessMode {102 Normal,103 WhiteList,104}105impl Default for AccessMode {106 fn default() -> Self {107 Self::Normal108 }109}110111#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub enum SchemaVersion {114 ImageURL,115 Unique,116}117impl Default for SchemaVersion {118 fn default() -> Self {119 Self::ImageURL120 }121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct Ownership<AccountId> {126 pub owner: AccountId,127 pub fraction: u128,128}129130#[derive(Encode, Decode, Debug, Clone, PartialEq)]131#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]132pub enum SponsorshipState<AccountId> {133 134 Disabled,135 Unconfirmed(AccountId),136 137 Confirmed(AccountId),138}139140impl<AccountId> SponsorshipState<AccountId> {141 fn sponsor(&self) -> Option<&AccountId> {142 match self {143 Self::Confirmed(sponsor) => Some(sponsor),144 _ => None,145 }146 }147148 fn pending_sponsor(&self) -> Option<&AccountId> {149 match self {150 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),151 _ => None,152 }153 }154155 fn confirmed(&self) -> bool {156 matches!(self, Self::Confirmed(_))157 }158}159160impl<T> Default for SponsorshipState<T> {161 fn default() -> Self {162 Self::Disabled163 }164}165166#[derive(Encode, Decode, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct Collection<T: Config> {169 pub owner: T::CrossAccountId,170 pub mode: CollectionMode,171 pub access: AccessMode,172 pub decimal_points: DecimalPoints,173 pub name: Vec<u16>, 174 pub description: Vec<u16>, 175 pub token_prefix: Vec<u8>, 176 pub mint_mode: bool,177 pub offchain_schema: Vec<u8>,178 pub schema_version: SchemaVersion,179 pub sponsorship: SponsorshipState<T::AccountId>,180 pub limits: CollectionLimits<T::BlockNumber>, 181 pub variable_on_chain_schema: Vec<u8>, 182 pub const_on_chain_schema: Vec<u8>, 183}184185pub struct CollectionHandle<T: Config> {186 pub id: CollectionId,187 collection: Collection<T>,188 logs: eth::log::LogRecorder,189}190impl<T: Config> CollectionHandle<T> {191 pub fn get(id: CollectionId) -> Option<Self> {192 <CollectionById<T>>::get(id)193 .map(|collection| Self {194 id,195 collection,196 logs: eth::log::LogRecorder::default(),197 })198 }199 pub fn log(&self, topics: Vec<H256>, data: eth::abi::AbiWriter) {200 self.logs.log(topics, data)201 }202 pub fn into_inner(self) -> Collection<T> {203 self.collection.clone()204 }205}206207impl<T: Config> Deref for CollectionHandle<T> {208 type Target = Collection<T>;209210 fn deref(&self) -> &Self::Target {211 &self.collection212 }213}214215impl<T: Config> DerefMut for CollectionHandle<T> {216 fn deref_mut(&mut self) -> &mut Self::Target {217 &mut self.collection218 }219}220221#[derive(Encode, Decode, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct NftItemType<AccountId> {224 pub owner: AccountId,225 pub const_data: Vec<u8>,226 pub variable_data: Vec<u8>,227}228229#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]230#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]231pub struct FungibleItemType {232 pub value: u128,233}234235#[derive(Encode, Decode, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct ReFungibleItemType<AccountId> {238 pub owner: Vec<Ownership<AccountId>>,239 pub const_data: Vec<u8>,240 pub variable_data: Vec<u8>,241}242243244245246247248249250251252253254#[derive(Encode, Decode, Debug, Clone, PartialEq)]255#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]256pub struct CollectionLimits<BlockNumber: Encode + Decode> {257 pub account_token_ownership_limit: u32,258 pub sponsored_data_size: u32,259 260 261 262 pub sponsored_data_rate_limit: Option<BlockNumber>,263 pub token_limit: u32,264265 266 pub sponsor_transfer_timeout: u32,267 pub owner_can_transfer: bool,268 pub owner_can_destroy: bool,269}270271impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {272 fn default() -> Self {273 Self { 274 account_token_ownership_limit: 10_000_000, 275 token_limit: u32::max_value(),276 sponsored_data_size: u32::MAX, 277 sponsored_data_rate_limit: None,278 sponsor_transfer_timeout: 14400,279 owner_can_transfer: true,280 owner_can_destroy: true281 }282 }283}284285#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]286#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]287pub struct ChainLimits {288 pub collection_numbers_limit: u32,289 pub account_token_ownership_limit: u32,290 pub collections_admins_limit: u64,291 pub custom_data_limit: u32,292293 294 pub nft_sponsor_transfer_timeout: u32,295 pub fungible_sponsor_transfer_timeout: u32,296 pub refungible_sponsor_transfer_timeout: u32,297298 299 pub offchain_schema_limit: u32,300 pub variable_on_chain_schema_limit: u32,301 pub const_on_chain_schema_limit: u32,302}303304pub trait WeightInfo {305 fn create_collection() -> Weight;306 fn destroy_collection() -> Weight;307 fn add_to_white_list() -> Weight;308 fn remove_from_white_list() -> Weight;309 fn set_public_access_mode() -> Weight;310 fn set_mint_permission() -> Weight;311 fn change_collection_owner() -> Weight;312 fn add_collection_admin() -> Weight;313 fn remove_collection_admin() -> Weight;314 fn set_collection_sponsor() -> Weight;315 fn confirm_sponsorship() -> Weight;316 fn remove_collection_sponsor() -> Weight;317 fn create_item(s: usize) -> Weight;318 fn burn_item() -> Weight;319 fn transfer() -> Weight;320 fn approve() -> Weight;321 fn transfer_from() -> Weight;322 fn set_offchain_schema() -> Weight;323 fn set_const_on_chain_schema() -> Weight;324 fn set_variable_on_chain_schema() -> Weight;325 fn set_variable_meta_data() -> Weight;326 fn enable_contract_sponsoring() -> Weight;327 fn set_schema_version() -> Weight;328 fn set_chain_limits() -> Weight;329 fn set_contract_sponsoring_rate_limit() -> Weight;330 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;331 fn toggle_contract_white_list() -> Weight;332 fn add_to_contract_white_list() -> Weight;333 fn remove_from_contract_white_list() -> Weight;334 fn set_collection_limits() -> Weight;335}336337#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]338#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]339pub struct CreateNftData {340 pub const_data: Vec<u8>,341 pub variable_data: Vec<u8>,342}343344#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]345#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]346pub struct CreateFungibleData {347 pub value: u128,348}349350#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]351#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]352pub struct CreateReFungibleData {353 pub const_data: Vec<u8>,354 pub variable_data: Vec<u8>,355 pub pieces: u128,356}357358#[derive(Encode, Decode, Debug, Clone, PartialEq)]359#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]360pub enum CreateItemData {361 NFT(CreateNftData),362 Fungible(CreateFungibleData),363 ReFungible(CreateReFungibleData),364}365366impl CreateItemData {367 pub fn len(&self) -> usize {368 let len = match self {369 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),370 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),371 _ => 0372 };373 374 return len;375 }376}377378impl From<CreateNftData> for CreateItemData {379 fn from(item: CreateNftData) -> Self {380 CreateItemData::NFT(item)381 }382}383384impl From<CreateReFungibleData> for CreateItemData {385 fn from(item: CreateReFungibleData) -> Self {386 CreateItemData::ReFungible(item)387 }388}389390impl From<CreateFungibleData> for CreateItemData {391 fn from(item: CreateFungibleData) -> Self {392 CreateItemData::Fungible(item)393 }394}395396397decl_error! {398 399 pub enum Error for Module<T: Config> {400 401 TotalCollectionsLimitExceeded,402 403 CollectionDecimalPointLimitExceeded, 404 405 CollectionNameLimitExceeded, 406 407 CollectionDescriptionLimitExceeded, 408 409 CollectionTokenPrefixLimitExceeded,410 411 CollectionNotFound,412 413 TokenNotFound,414 415 AdminNotFound,416 417 NumOverflow, 418 419 AlreadyAdmin, 420 421 NoPermission,422 423 ConfirmUnsetSponsorFail,424 425 PublicMintingNotAllowed,426 427 MustBeTokenOwner,428 429 TokenValueTooLow,430 431 NftSizeLimitExceeded,432 433 ApproveNotFound,434 435 TokenValueNotEnough,436 437 ApproveRequired,438 439 AddresNotInWhiteList,440 441 CollectionAdminsLimitExceeded,442 443 AddressOwnershipLimitExceeded,444 445 EmptyArgument,446 447 TokenConstDataLimitExceeded,448 449 TokenVariableDataLimitExceeded,450 451 NotNftDataUsedToMintNftCollectionToken,452 453 NotFungibleDataUsedToMintFungibleCollectionToken,454 455 NotReFungibleDataUsedToMintReFungibleCollectionToken,456 457 UnexpectedCollectionType,458 459 CantStoreMetadataInFungibleTokens,460 461 CollectionTokenLimitExceeded,462 463 AccountTokenLimitExceeded,464 465 CollectionLimitBoundsExceeded,466 467 OwnerPermissionsCantBeReverted,468 469 SchemaDataLimitExceeded,470 471 WrongRefungiblePieces,472 473 BadCreateRefungibleCall,474 }475}476477pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {478 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;479480 481 type WeightInfo: WeightInfo;482483 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;484 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;485 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;486487 type CrossAccountId: CrossAccountId<Self::AccountId>;488 type Currency: Currency<Self::AccountId>;489 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;490 type TreasuryAccountId: Get<Self::AccountId>;491492 type EthereumChainId: Get<u64>;493 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;494}495496#[cfg(feature = "runtime-benchmarks")]497mod benchmarking;498499500501502503504505506507508509510511512513514515516517518519520521522523decl_storage! {524 trait Store for Module<T: Config> as Nft {525526 527 528 CreatedCollectionCount: u32;529 530 ChainVersion: u64;531 532 533 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;534 535536 537 pub ChainLimit get(fn chain_limit) config(): ChainLimits;538 539540 541 542 543 DestroyedCollectionCount: u32;544 545 546 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;547 548549 550 551 552 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;553 554 555 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;556 557 558 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;559 560561 562 563 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;564565 566 567 568 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;569570 571 572 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;573 574 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;575 576 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;577 578579 580 581 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;582 583584 585 586 587 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;588 589 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;590 591 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;592 593 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;594 595596 597 598 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;599 600 601 602 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;603 604 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;605 606 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;607 608 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;609 610 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 611 612 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 613 614 }615 add_extra_genesis {616 build(|config: &GenesisConfig<T>| {617 618 for (_num, _c) in &config.collection_id {619 <Module<T>>::init_collection(_c);620 }621622 for (_num, _c, _i) in &config.nft_item_id {623 <Module<T>>::init_nft_token(*_c, _i);624 }625626 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {627 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);628 }629630 for (_num, _c, _i) in &config.refungible_item_id {631 <Module<T>>::init_refungible_token(*_c, _i);632 }633 })634 }635}636637decl_event!(638 pub enum Event<T>639 where640 CrossAccountId = <T as Config>::CrossAccountId,641 {642 643 644 645 646 647 648 649 650 651 CollectionCreated(CollectionId, u8, CrossAccountId),652653 654 655 656 657 658 659 660 661 662 ItemCreated(CollectionId, TokenId, CrossAccountId),663664 665 666 667 668 669 670 671 ItemDestroyed(CollectionId, TokenId),672673 674 675 676 677 678 679 680 681 682 683 684 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),685686 687 688 689 690 691 692 693 694 695 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),696 }697);698699decl_module! {700 pub struct Module<T: Config> for enum Call 701 where 702 origin: T::Origin703 {704 fn deposit_event() = default;705 type Error = Error<T>;706707 fn on_initialize(now: T::BlockNumber) -> Weight {708 0709 }710711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 #[weight = <T as Config>::WeightInfo::create_collection()]728 #[transactional]729 pub fn create_collection(origin,730 collection_name: Vec<u16>,731 collection_description: Vec<u16>,732 token_prefix: Vec<u8>,733 mode: CollectionMode) -> DispatchResult {734735 736 let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);737738 739 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();740 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(741 &T::TreasuryAccountId::get(),742 T::CollectionCreationPrice::get(),743 ));744 <T as Config>::Currency::settle(745 who.as_sub(),746 imbalance,747 WithdrawReasons::TRANSFER,748 ExistenceRequirement::KeepAlive,749 ).map_err(|_| Error::<T>::NoPermission)?;750751 let decimal_points = match mode {752 CollectionMode::Fungible(points) => points,753 _ => 0754 };755756 let chain_limit = ChainLimit::get();757758 let created_count = CreatedCollectionCount::get();759 let destroyed_count = DestroyedCollectionCount::get();760761 762 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);763764 765 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);766 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);767 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);768 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);769770 771 let next_id = created_count772 .checked_add(1)773 .ok_or(Error::<T>::NumOverflow)?;774775 CreatedCollectionCount::put(next_id);776777 let limits = CollectionLimits {778 sponsored_data_size: chain_limit.custom_data_limit,779 ..Default::default()780 };781782 783 let new_collection = Collection {784 owner: who.clone(),785 name: collection_name,786 mode: mode.clone(),787 mint_mode: false,788 access: AccessMode::Normal,789 description: collection_description,790 decimal_points: decimal_points,791 token_prefix: token_prefix,792 offchain_schema: Vec::new(),793 schema_version: SchemaVersion::ImageURL,794 sponsorship: SponsorshipState::Disabled,795 variable_on_chain_schema: Vec::new(),796 const_on_chain_schema: Vec::new(),797 limits,798 };799800 801 <CollectionById<T>>::insert(next_id, new_collection);802803 804 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));805806 Ok(())807 }808809 810 811 812 813 814 815 816 817 818 #[weight = <T as Config>::WeightInfo::destroy_collection()]819 #[transactional]820 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {821822 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);823 let collection = Self::get_collection(collection_id)?;824 Self::check_owner_permissions(&collection, &sender)?;825 if !collection.limits.owner_can_destroy {826 fail!(Error::<T>::NoPermission);827 }828829 <AddressTokens<T>>::remove_prefix(collection_id);830 <Allowances<T>>::remove_prefix(collection_id);831 <Balance<T>>::remove_prefix(collection_id);832 <ItemListIndex>::remove(collection_id);833 <AdminList<T>>::remove(collection_id);834 <CollectionById<T>>::remove(collection_id);835 <WhiteList<T>>::remove_prefix(collection_id);836837 <NftItemList<T>>::remove_prefix(collection_id);838 <FungibleItemList<T>>::remove_prefix(collection_id);839 <ReFungibleItemList<T>>::remove_prefix(collection_id);840841 <NftTransferBasket<T>>::remove_prefix(collection_id);842 <FungibleTransferBasket<T>>::remove_prefix(collection_id);843 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);844845 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);846847 DestroyedCollectionCount::put(DestroyedCollectionCount::get()848 .checked_add(1)849 .ok_or(Error::<T>::NumOverflow)?);850851 Ok(())852 }853854 855 856 857 858 859 860 861 862 863 864 865 866 #[weight = <T as Config>::WeightInfo::add_to_white_list()]867 #[transactional]868 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{869870 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);871 let collection = Self::get_collection(collection_id)?;872 Self::check_owner_or_admin_permissions(&collection, &sender)?;873874 <WhiteList<T>>::insert(collection_id, address.as_sub(), true);875 876 Ok(())877 }878879 880 881 882 883 884 885 886 887 888 889 890 891 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]892 #[transactional]893 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{894895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897 Self::check_owner_or_admin_permissions(&collection, &sender)?;898899 <WhiteList<T>>::remove(collection_id, address.as_sub());900901 Ok(())902 }903904 905 906 907 908 909 910 911 912 913 914 915 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]916 #[transactional]917 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult918 {919 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);920921 let mut target_collection = Self::get_collection(collection_id)?;922 Self::check_owner_permissions(&target_collection, &sender)?;923 target_collection.access = mode;924 Self::save_collection(target_collection);925926 Ok(())927 }928929 930 931 932 933 934 935 936 937 938 939 940 941 942 #[weight = <T as Config>::WeightInfo::set_mint_permission()]943 #[transactional]944 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult945 {946 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);947948 let mut target_collection = Self::get_collection(collection_id)?;949 Self::check_owner_permissions(&target_collection, &sender)?;950 target_collection.mint_mode = mint_permission;951 Self::save_collection(target_collection);952953 Ok(())954 }955956 957 958 959 960 961 962 963 964 965 966 967 #[weight = <T as Config>::WeightInfo::change_collection_owner()]968 #[transactional]969 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {970971 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);972 let mut target_collection = Self::get_collection(collection_id)?;973 Self::check_owner_permissions(&target_collection, &sender)?;974 target_collection.owner = new_owner;975 Self::save_collection(target_collection);976977 Ok(())978 }979980 981 982 983 984 985 986 987 988 989 990 991 992 993 #[weight = <T as Config>::WeightInfo::add_collection_admin()]994 #[transactional]995 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {996 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);997 let collection = Self::get_collection(collection_id)?;998 Self::check_owner_or_admin_permissions(&collection, &sender)?;999 let mut admin_arr = <AdminList<T>>::get(collection_id);10001001 match admin_arr.binary_search(&new_admin_id) {1002 Ok(_) => {},1003 Err(idx) => {1004 let limits = ChainLimit::get();1005 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1006 admin_arr.insert(idx, new_admin_id);1007 <AdminList<T>>::insert(collection_id, admin_arr);1008 }1009 }1010 Ok(())1011 }10121013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1026 #[transactional]1027 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1028 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1029 let collection = Self::get_collection(collection_id)?;1030 Self::check_owner_or_admin_permissions(&collection, &sender)?;1031 let mut admin_arr = <AdminList<T>>::get(collection_id);10321033 match admin_arr.binary_search(&account_id) {1034 Ok(idx) => {1035 admin_arr.remove(idx);1036 <AdminList<T>>::insert(collection_id, admin_arr);1037 },1038 Err(_) => {}1039 }1040 Ok(())1041 }10421043 1044 1045 1046 1047 1048 1049 1050 1051 1052 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1053 #[transactional]1054 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1055 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1056 let mut target_collection = Self::get_collection(collection_id)?;1057 Self::check_owner_permissions(&target_collection, &sender)?;10581059 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1060 Self::save_collection(target_collection);10611062 Ok(())1063 }10641065 1066 1067 1068 1069 1070 1071 1072 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1073 #[transactional]1074 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1075 let sender = ensure_signed(origin)?;10761077 let mut target_collection = Self::get_collection(collection_id)?;1078 ensure!(1079 target_collection.sponsorship.pending_sponsor() == Some(&sender),1080 Error::<T>::ConfirmUnsetSponsorFail1081 );10821083 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1084 Self::save_collection(target_collection);10851086 Ok(())1087 }10881089 1090 1091 1092 1093 1094 1095 1096 1097 1098 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1099 #[transactional]1100 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1101 let sender = ensure_signed(origin)?;11021103 let mut target_collection = Self::get_collection(collection_id)?;1104 Self::check_owner_permissions(&target_collection, &T::CrossAccountId::from_sub(sender))?;11051106 target_collection.sponsorship = SponsorshipState::Disabled;1107 Self::save_collection(target_collection);11081109 Ok(())1110 }11111112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 11351136 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1137 #[transactional]1138 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {11391140 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11411142 let target_collection = Self::get_collection(collection_id)?;11431144 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1145 Self::validate_create_item_args(&target_collection, &data)?;1146 Self::create_item_no_validation(&target_collection, owner, data)?;11471148 Self::submit_logs(target_collection)?;1149 Ok(())1150 }11511152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1171 .map(|data| { data.len() })1172 .sum())]1173 #[transactional]1174 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11751176 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1177 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1178 let collection = Self::get_collection(collection_id)?;11791180 Self::create_multiple_items_internal(sender, &collection, owner, items_data)?;11811182 Self::submit_logs(collection)?;1183 Ok(())1184 }11851186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 #[weight = <T as Config>::WeightInfo::burn_item()]1200 #[transactional]1201 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12021203 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1204 let target_collection = Self::get_collection(collection_id)?;12051206 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12071208 Self::submit_logs(target_collection)?;1209 Ok(())1210 }12111212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 #[weight = <T as Config>::WeightInfo::transfer()]1236 #[transactional]1237 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1238 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1239 let collection = Self::get_collection(collection_id)?;12401241 Self::transfer_internal(sender, recipient, &collection, item_id, value)?;12421243 Self::submit_logs(collection)?;1244 Ok(())1245 }12461247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 #[weight = <T as Config>::WeightInfo::approve()]1263 #[transactional]1264 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12651266 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1267 let collection = Self::get_collection(collection_id)?;12681269 Self::approve_internal(sender, spender, &collection, item_id, amount)?;12701271 Self::submit_logs(collection)?;1272 Ok(())1273 }1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 #[weight = <T as Config>::WeightInfo::transfer_from()]1295 #[transactional]1296 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12971298 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1299 let collection = Self::get_collection(collection_id)?;13001301 Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?;13021303 Self::submit_logs(collection)?;1304 Ok(())1305 }13061307 1308 13091310 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 = ensure_signed(origin)?;1343 1344 let target_collection = Self::get_collection(collection_id)?;1345 Self::token_exists(&target_collection, item_id)?;13461347 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13481349 1350 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1351 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1352 Error::<T>::NoPermission);13531354 match target_collection.mode1355 {1356 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1357 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1358 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1359 _ => fail!(Error::<T>::UnexpectedCollectionType)1360 };13611362 Ok(())1363 }1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 #[weight = <T as Config>::WeightInfo::set_schema_version()]1380 #[transactional]1381 pub fn set_schema_version(1382 origin,1383 collection_id: CollectionId,1384 version: SchemaVersion1385 ) -> DispatchResult {1386 let sender = ensure_signed(origin)?;1387 let mut target_collection = Self::get_collection(collection_id)?;1388 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1389 target_collection.schema_version = version;1390 Self::save_collection(target_collection);13911392 Ok(())1393 }13941395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1408 #[transactional]1409 pub fn set_offchain_schema(1410 origin,1411 collection_id: CollectionId,1412 schema: Vec<u8>1413 ) -> DispatchResult {1414 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1415 let mut target_collection = Self::get_collection(collection_id)?;1416 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14171418 1419 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14201421 target_collection.offchain_schema = schema;1422 Self::save_collection(target_collection);14231424 Ok(())1425 }14261427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1440 #[transactional]1441 pub fn set_const_on_chain_schema (1442 origin,1443 collection_id: CollectionId,1444 schema: Vec<u8>1445 ) -> DispatchResult {1446 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1447 let mut target_collection = Self::get_collection(collection_id)?;1448 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14491450 1451 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14521453 target_collection.const_on_chain_schema = schema;1454 Self::save_collection(target_collection);14551456 Ok(())1457 }14581459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1472 #[transactional]1473 pub fn set_variable_on_chain_schema (1474 origin,1475 collection_id: CollectionId,1476 schema: Vec<u8>1477 ) -> DispatchResult {1478 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1479 let mut target_collection = Self::get_collection(collection_id)?;1480 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14811482 1483 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14841485 target_collection.variable_on_chain_schema = schema;1486 Self::save_collection(target_collection);14871488 Ok(())1489 }14901491 1492 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1493 #[transactional]1494 pub fn set_chain_limits(1495 origin,1496 limits: ChainLimits1497 ) -> DispatchResult {14981499 #[cfg(not(feature = "runtime-benchmarks"))]1500 ensure_root(origin)?;15011502 <ChainLimit>::put(limits);1503 Ok(())1504 }15051506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1518 #[transactional]1519 pub fn enable_contract_sponsoring(1520 origin,1521 contract_address: T::AccountId,1522 enable: bool1523 ) -> DispatchResult {15241525 let sender = ensure_signed(origin)?;15261527 #[cfg(feature = "runtime-benchmarks")]1528 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15291530 Self::ensure_contract_owned(sender, &contract_address)?;15311532 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1533 Ok(())1534 }15351536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1554 #[transactional]1555 pub fn set_contract_sponsoring_rate_limit(1556 origin,1557 contract_address: T::AccountId,1558 rate_limit: T::BlockNumber1559 ) -> DispatchResult {1560 let sender = ensure_signed(origin)?;15611562 #[cfg(feature = "runtime-benchmarks")]1563 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15641565 Self::ensure_contract_owned(sender, &contract_address)?;1566 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1567 Ok(())1568 }15691570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1582 #[transactional]1583 pub fn toggle_contract_white_list(1584 origin,1585 contract_address: T::AccountId,1586 enable: bool1587 ) -> DispatchResult {1588 let sender = ensure_signed(origin)?;15891590 #[cfg(feature = "runtime-benchmarks")]1591 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15921593 Self::ensure_contract_owned(sender, &contract_address)?;1594 if enable {1595 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1596 } else {1597 <ContractWhiteListEnabled<T>>::remove(contract_address);1598 }1599 Ok(())1600 }1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1614 #[transactional]1615 pub fn add_to_contract_white_list(1616 origin,1617 contract_address: T::AccountId,1618 account_address: T::AccountId1619 ) -> DispatchResult {1620 let sender = ensure_signed(origin)?;16211622 #[cfg(feature = "runtime-benchmarks")]1623 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1624 1625 Self::ensure_contract_owned(sender, &contract_address)?; 1626 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1627 Ok(())1628 }16291630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1642 #[transactional]1643 pub fn remove_from_contract_white_list(1644 origin,1645 contract_address: T::AccountId,1646 account_address: T::AccountId1647 ) -> DispatchResult {1648 let sender = ensure_signed(origin)?;16491650 #[cfg(feature = "runtime-benchmarks")]1651 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16521653 Self::ensure_contract_owned(sender, &contract_address)?;1654 <ContractWhiteList<T>>::remove(contract_address, account_address);1655 Ok(())1656 }16571658 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1659 #[transactional]1660 pub fn set_collection_limits(1661 origin,1662 collection_id: u32,1663 new_limits: CollectionLimits<T::BlockNumber>,1664 ) -> DispatchResult {1665 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1666 let mut target_collection = Self::get_collection(collection_id)?;1667 Self::check_owner_permissions(&target_collection, &sender)?;1668 let old_limits = &target_collection.limits;1669 let chain_limits = ChainLimit::get();16701671 1672 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1673 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1674 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1675 Error::<T>::CollectionLimitBoundsExceeded);16761677 1678 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1679 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16801681 ensure!(1682 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1683 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1684 Error::<T>::OwnerPermissionsCantBeReverted,1685 );16861687 target_collection.limits = new_limits;1688 Self::save_collection(target_collection);16891690 Ok(())1691 } 1692 }1693}16941695impl<T: Config> Module<T> {16961697 pub fn transfer_internal(sender: T::CrossAccountId, recipient: T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1698 1699 Self::is_correct_transfer(target_collection, &recipient)?;17001701 1702 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1703 Self::is_owner_or_admin_permissions(target_collection, &sender),1704 Error::<T>::NoPermission);17051706 if target_collection.access == AccessMode::WhiteList {1707 Self::check_white_list(target_collection, &sender)?;1708 Self::check_white_list(target_collection, &recipient)?;1709 }17101711 match target_collection.mode1712 {1713 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1714 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1715 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1716 _ => ()1717 };17181719 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17201721 Ok(())1722 }17231724 pub fn approve_internal(1725 sender: T::CrossAccountId,1726 spender: T::CrossAccountId,1727 collection: &CollectionHandle<T>,1728 item_id: TokenId,1729 amount: u1281730 ) -> DispatchResult {1731 Self::token_exists(&collection, item_id)?;17321733 1734 let bypasses_limits = collection.limits.owner_can_transfer &&1735 Self::is_owner_or_admin_permissions(1736 &collection,1737 &sender,1738 );17391740 let allowance_limit = if bypasses_limits {1741 None1742 } else if let Some(amount) = Self::owned_amount(1743 &sender,1744 &collection,1745 item_id,1746 ) {1747 Some(amount)1748 } else {1749 fail!(Error::<T>::NoPermission);1750 };17511752 if collection.access == AccessMode::WhiteList {1753 Self::check_white_list(&collection, &sender)?;1754 Self::check_white_list(&collection, &spender)?;1755 }17561757 let allowance: u128 = amount1758 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1759 .ok_or(Error::<T>::NumOverflow)?;1760 if let Some(limit) = allowance_limit {1761 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1762 }1763 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17641765 if matches!(collection.mode, CollectionMode::NFT) {1766 1767 collection.log(1768 Vec::from([1769 eth::APPROVAL_NFT_TOPIC,1770 eth::address_to_topic(sender.as_eth()),1771 eth::address_to_topic(spender.as_eth()),1772 ]),1773 abi_encode!(uint256(item_id.into())),1774 );1775 }17761777 if matches!(collection.mode, CollectionMode::Fungible(_)) {1778 1779 collection.log(1780 Vec::from([1781 eth::APPROVAL_FUNGIBLE_TOPIC,1782 eth::address_to_topic(sender.as_eth()),1783 eth::address_to_topic(spender.as_eth()),1784 ]),1785 abi_encode!(uint256(allowance.into())),1786 );1787 }17881789 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1790 Ok(())1791 }17921793 pub fn transfer_from_internal(1794 sender: T::CrossAccountId,1795 from: T::CrossAccountId,1796 recipient: T::CrossAccountId,1797 collection: &CollectionHandle<T>,1798 item_id: TokenId,1799 amount: u128,1800 ) -> DispatchResult {1801 1802 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));18031804 1805 Self::is_correct_transfer(&collection, &recipient)?;18061807 1808 ensure!(1809 approval >= amount || 1810 (1811 collection.limits.owner_can_transfer &&1812 Self::is_owner_or_admin_permissions(&collection, &sender)1813 ),1814 Error::<T>::NoPermission1815 );18161817 if collection.access == AccessMode::WhiteList {1818 Self::check_white_list(&collection, &sender)?;1819 Self::check_white_list(&collection, &recipient)?;1820 }18211822 1823 let allowance = approval.saturating_sub(amount);1824 if allowance > 0 {1825 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1826 } else {1827 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1828 }18291830 match collection.mode {1831 CollectionMode::NFT => {1832 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1833 }1834 CollectionMode::Fungible(_) => {1835 Self::transfer_fungible(&collection, amount, &from, &recipient)?1836 }1837 CollectionMode::ReFungible => {1838 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1839 }1840 _ => ()1841 };18421843 if matches!(collection.mode, CollectionMode::Fungible(_)) {1844 collection.log(1845 Vec::from([1846 eth::APPROVAL_FUNGIBLE_TOPIC,1847 eth::address_to_topic(from.as_eth()),1848 eth::address_to_topic(sender.as_eth()),1849 ]),1850 abi_encode!(uint256(allowance.into())),1851 );1852 }18531854 Ok(())1855 }18561857 pub fn create_multiple_items_internal(1858 sender: T::CrossAccountId,1859 collection: &CollectionHandle<T>,1860 owner: T::CrossAccountId,1861 items_data: Vec<CreateItemData>,1862 ) -> DispatchResult {1863 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18641865 for data in &items_data {1866 Self::validate_create_item_args(&collection, data)?;1867 }1868 for data in &items_data {1869 Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1870 }18711872 Ok(())1873 }18741875 pub fn burn_item_internal(1876 sender: &T::CrossAccountId,1877 collection: &CollectionHandle<T>,1878 item_id: TokenId,1879 value: u128,1880 ) -> DispatchResult {1881 ensure!(1882 Self::is_item_owner(&sender, &collection, item_id) ||1883 (1884 collection.limits.owner_can_transfer &&1885 Self::is_owner_or_admin_permissions(&collection, &sender)1886 ),1887 Error::<T>::NoPermission1888 );18891890 if collection.access == AccessMode::WhiteList {1891 Self::check_white_list(&collection, &sender)?;1892 }18931894 match collection.mode1895 {1896 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1897 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1898 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1899 _ => ()1900 };19011902 Ok(())1903 }19041905 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1906 let collection_id = collection.id;19071908 1909 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1910 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1911 1912 Ok(())1913 }19141915 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1916 let collection_id = collection.id;19171918 1919 let total_items: u32 = ItemListIndex::get(collection_id)1920 .checked_add(amount)1921 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1922 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1923 .checked_add(amount)1924 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1925 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1926 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19271928 if !Self::is_owner_or_admin_permissions(collection, &sender) {1929 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1930 Self::check_white_list(collection, owner)?;1931 Self::check_white_list(collection, sender)?;1932 }19331934 Ok(())1935 }19361937 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1938 match target_collection.mode1939 {1940 CollectionMode::NFT => {1941 if let CreateItemData::NFT(data) = data {1942 1943 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1944 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1945 } else {1946 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1947 }1948 },1949 CollectionMode::Fungible(_) => {1950 if let CreateItemData::Fungible(_) = data {1951 } else {1952 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1953 }1954 },1955 CollectionMode::ReFungible => {1956 if let CreateItemData::ReFungible(data) = data {19571958 1959 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1960 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19611962 1963 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1964 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1965 } else {1966 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1967 }1968 },1969 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1970 };19711972 Ok(())1973 }19741975 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1976 match data1977 {1978 CreateItemData::NFT(data) => {1979 let item = NftItemType {1980 owner: owner.clone(),1981 const_data: data.const_data,1982 variable_data: data.variable_data1983 };19841985 Self::add_nft_item(collection, item)?;1986 },1987 CreateItemData::Fungible(data) => {1988 Self::add_fungible_item(collection, &owner, data.value)?;1989 },1990 CreateItemData::ReFungible(data) => {1991 let mut owner_list = Vec::new();1992 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});19931994 let item = ReFungibleItemType {1995 owner: owner_list,1996 const_data: data.const_data,1997 variable_data: data.variable_data1998 };19992000 Self::add_refungible_item(collection, item)?;2001 }2002 };20032004 Ok(())2005 }20062007 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2008 let collection_id = collection.id;20092010 2011 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20122013 2014 let item = FungibleItemType {2015 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2016 };2017 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20182019 2020 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2021 .checked_add(value)2022 .ok_or(Error::<T>::NumOverflow)?;2023 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20242025 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2026 Ok(())2027 }20282029 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2030 let collection_id = collection.id;20312032 let current_index = <ItemListIndex>::get(collection_id)2033 .checked_add(1)2034 .ok_or(Error::<T>::NumOverflow)?;2035 let itemcopy = item.clone();20362037 ensure!(2038 item.owner.len() == 1,2039 Error::<T>::BadCreateRefungibleCall,2040 );2041 let item_owner = item.owner.first().expect("only one owner is defined");20422043 let value = item_owner.fraction;2044 let owner = item_owner.owner.clone();20452046 Self::add_token_index(collection_id, current_index, &owner)?;20472048 <ItemListIndex>::insert(collection_id, current_index);2049 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20502051 2052 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2053 .checked_add(value)2054 .ok_or(Error::<T>::NumOverflow)?;2055 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20562057 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2058 Ok(())2059 }20602061 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2062 let collection_id = collection.id;20632064 let current_index = <ItemListIndex>::get(collection_id)2065 .checked_add(1)2066 .ok_or(Error::<T>::NumOverflow)?;20672068 let item_owner = item.owner.clone();2069 Self::add_token_index(collection_id, current_index, &item.owner)?;20702071 <ItemListIndex>::insert(collection_id, current_index);2072 <NftItemList<T>>::insert(collection_id, current_index, item);20732074 2075 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2076 .checked_add(1)2077 .ok_or(Error::<T>::NumOverflow)?;2078 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);20792080 collection.log(2081 Vec::from([2082 eth::TRANSFER_NFT_TOPIC,2083 eth::address_to_topic(&H160::default()),2084 eth::address_to_topic(item_owner.as_eth()),2085 ]),2086 abi_encode!(uint256(current_index.into())),2087 );2088 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2089 Ok(())2090 }20912092 fn burn_refungible_item(2093 collection: &CollectionHandle<T>,2094 item_id: TokenId,2095 owner: &T::CrossAccountId,2096 ) -> DispatchResult {2097 let collection_id = collection.id;20982099 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2100 .ok_or(Error::<T>::TokenNotFound)?;2101 let rft_balance = token2102 .owner2103 .iter()2104 .find(|&i| i.owner == *owner)2105 .ok_or(Error::<T>::TokenNotFound)?;2106 Self::remove_token_index(collection_id, item_id, owner)?;21072108 2109 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2110 .checked_sub(rft_balance.fraction)2111 .ok_or(Error::<T>::NumOverflow)?;2112 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21132114 2115 let index = token2116 .owner2117 .iter()2118 .position(|i| i.owner == *owner)2119 .expect("owned item is exists");2120 token.owner.remove(index);2121 let owner_count = token.owner.len();21222123 2124 if owner_count == 0 {2125 <ReFungibleItemList<T>>::remove(collection_id, item_id);2126 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2127 }2128 else {2129 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2130 }21312132 Ok(())2133 }21342135 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2136 let collection_id = collection.id;21372138 let item = <NftItemList<T>>::get(collection_id, item_id)2139 .ok_or(Error::<T>::TokenNotFound)?;2140 Self::remove_token_index(collection_id, item_id, &item.owner)?;21412142 2143 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2144 .checked_sub(1)2145 .ok_or(Error::<T>::NumOverflow)?;2146 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2147 <NftItemList<T>>::remove(collection_id, item_id);2148 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21492150 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2151 Ok(())2152 }21532154 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2155 let collection_id = collection.id;21562157 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2158 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21592160 2161 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2162 .checked_sub(value)2163 .ok_or(Error::<T>::NumOverflow)?;2164 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21652166 if balance.value - value > 0 {2167 balance.value -= value;2168 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2169 }2170 else {2171 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2172 }21732174 collection.log(2175 Vec::from([2176 eth::TRANSFER_FUNGIBLE_TOPIC,2177 eth::address_to_topic(owner.as_eth()),2178 eth::address_to_topic(&H160::default()),2179 ]),2180 abi_encode!(uint256(value.into())),2181 );2182 Ok(())2183 }21842185 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2186 Ok(<CollectionHandle<T>>::get(collection_id)2187 .ok_or(Error::<T>::CollectionNotFound)?)2188 }21892190 fn save_collection(collection: CollectionHandle<T>) {2191 <CollectionById<T>>::insert(collection.id, collection.into_inner());2192 }21932194 fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2195 T::EthereumTransactionSender::submit_logs_transaction(2196 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2197 collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2198 )2199 }22002201 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2202 ensure!(2203 *subject == target_collection.owner,2204 Error::<T>::NoPermission2205 );22062207 Ok(())2208 }22092210 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2211 *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2212 }22132214 fn check_owner_or_admin_permissions(2215 collection: &CollectionHandle<T>,2216 subject: &T::CrossAccountId,2217 ) -> DispatchResult {2218 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22192220 Ok(())2221 }22222223 fn owned_amount(2224 subject: &T::CrossAccountId,2225 target_collection: &CollectionHandle<T>,2226 item_id: TokenId,2227 ) -> Option<u128> {2228 let collection_id = target_collection.id;22292230 match target_collection.mode {2231 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2232 .then(|| 1),2233 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2234 .value),2235 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2236 .owner2237 .iter()2238 .find(|i| i.owner == *subject)2239 .map(|i| i.fraction),2240 CollectionMode::Invalid => None,2241 }2242 }22432244 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2245 match target_collection.mode {2246 CollectionMode::Fungible(_) => true,2247 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2248 }2249 }22502251 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2252 let collection_id = collection.id;22532254 let mes = Error::<T>::AddresNotInWhiteList;2255 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22562257 Ok(())2258 }22592260 2261 2262 fn token_exists(2263 target_collection: &CollectionHandle<T>,2264 item_id: TokenId,2265 ) -> DispatchResult {2266 let collection_id = target_collection.id;2267 let exists = match target_collection.mode2268 {2269 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2270 CollectionMode::Fungible(_) => true,2271 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2272 _ => false2273 };22742275 ensure!(exists == true, Error::<T>::TokenNotFound);2276 Ok(())2277 }22782279 fn transfer_fungible(2280 collection: &CollectionHandle<T>,2281 value: u128,2282 owner: &T::CrossAccountId,2283 recipient: &T::CrossAccountId,2284 ) -> DispatchResult {2285 let collection_id = collection.id;22862287 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2288 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);22892290 2291 Self::add_fungible_item(collection, recipient, value)?;22922293 2294 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);22952296 2297 if balance.value == value {2298 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2299 }2300 else {2301 balance.value -= value;2302 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2303 }23042305 collection.log(2306 Vec::from([2307 eth::TRANSFER_FUNGIBLE_TOPIC,2308 eth::address_to_topic(owner.as_eth()),2309 eth::address_to_topic(recipient.as_eth()),2310 ]),2311 abi_encode!(uint256(value.into())),2312 );2313 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23142315 Ok(())2316 }23172318 fn transfer_refungible(2319 collection: &CollectionHandle<T>,2320 item_id: TokenId,2321 value: u128,2322 owner: T::CrossAccountId,2323 new_owner: T::CrossAccountId,2324 ) -> DispatchResult {2325 let collection_id = collection.id;2326 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2327 .ok_or(Error::<T>::TokenNotFound)?;23282329 let item = full_item2330 .owner2331 .iter()2332 .filter(|i| i.owner == owner)2333 .next()2334 .ok_or(Error::<T>::TokenNotFound)?;2335 let amount = item.fraction;23362337 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23382339 2340 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2341 .checked_sub(value)2342 .ok_or(Error::<T>::NumOverflow)?;2343 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23442345 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2346 .checked_add(value)2347 .ok_or(Error::<T>::NumOverflow)?;2348 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23492350 let old_owner = item.owner.clone();2351 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23522353 2354 if amount == value && !new_owner_has_account {2355 2356 2357 let mut new_full_item = full_item.clone();2358 new_full_item2359 .owner2360 .iter_mut()2361 .find(|i| i.owner == owner)2362 .expect("old owner does present in refungible")2363 .owner = new_owner.clone();2364 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23652366 2367 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2368 } else {2369 let mut new_full_item = full_item.clone();2370 new_full_item2371 .owner2372 .iter_mut()2373 .find(|i| i.owner == owner)2374 .expect("old owner does present in refungible")2375 .fraction -= value;23762377 2378 if new_owner_has_account {2379 2380 new_full_item2381 .owner2382 .iter_mut()2383 .find(|i| i.owner == new_owner)2384 .expect("new owner has account")2385 .fraction += value;2386 } else {2387 2388 new_full_item.owner.push(Ownership {2389 owner: new_owner.clone(),2390 fraction: value,2391 });2392 Self::add_token_index(collection_id, item_id, &new_owner)?;2393 }23942395 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2396 }23972398 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));23992400 Ok(())2401 }24022403 fn transfer_nft(2404 collection: &CollectionHandle<T>,2405 item_id: TokenId,2406 sender: T::CrossAccountId,2407 new_owner: T::CrossAccountId,2408 ) -> DispatchResult {2409 let collection_id = collection.id;2410 let mut item = <NftItemList<T>>::get(collection_id, item_id)2411 .ok_or(Error::<T>::TokenNotFound)?;24122413 ensure!(2414 sender == item.owner,2415 Error::<T>::MustBeTokenOwner2416 );24172418 2419 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2420 .checked_sub(1)2421 .ok_or(Error::<T>::NumOverflow)?;2422 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24232424 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2425 .checked_add(1)2426 .ok_or(Error::<T>::NumOverflow)?;2427 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24282429 2430 let old_owner = item.owner.clone();2431 item.owner = new_owner.clone();2432 <NftItemList<T>>::insert(collection_id, item_id, item);24332434 2435 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24362437 collection.log(2438 Vec::from([2439 eth::TRANSFER_NFT_TOPIC,2440 eth::address_to_topic(sender.as_eth()),2441 eth::address_to_topic(new_owner.as_eth()),2442 ]),2443 abi_encode!(uint256(item_id.into())),2444 );2445 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24462447 Ok(())2448 }2449 2450 fn set_re_fungible_variable_data(2451 collection: &CollectionHandle<T>,2452 item_id: TokenId,2453 data: Vec<u8>2454 ) -> DispatchResult {2455 let collection_id = collection.id;2456 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2457 .ok_or(Error::<T>::TokenNotFound)?;24582459 item.variable_data = data;24602461 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24622463 Ok(())2464 }24652466 fn set_nft_variable_data(2467 collection: &CollectionHandle<T>,2468 item_id: TokenId,2469 data: Vec<u8>2470 ) -> DispatchResult {2471 let collection_id = collection.id;2472 let mut item = <NftItemList<T>>::get(collection_id, item_id)2473 .ok_or(Error::<T>::TokenNotFound)?;2474 2475 item.variable_data = data;24762477 <NftItemList<T>>::insert(collection_id, item_id, item);2478 2479 Ok(())2480 }24812482 fn init_collection(item: &Collection<T>) {2483 2484 assert!(2485 item.decimal_points <= MAX_DECIMAL_POINTS,2486 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2487 );2488 assert!(2489 item.name.len() <= 64,2490 "Collection name can not be longer than 63 char"2491 );2492 assert!(2493 item.name.len() <= 256,2494 "Collection description can not be longer than 255 char"2495 );2496 assert!(2497 item.token_prefix.len() <= 16,2498 "Token prefix can not be longer than 15 char"2499 );25002501 2502 let next_id = CreatedCollectionCount::get()2503 .checked_add(1)2504 .unwrap();25052506 CreatedCollectionCount::put(next_id);2507 }25082509 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2510 let current_index = <ItemListIndex>::get(collection_id)2511 .checked_add(1)2512 .unwrap();25132514 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25152516 <ItemListIndex>::insert(collection_id, current_index);25172518 2519 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2520 .checked_add(1)2521 .unwrap();2522 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2523 }25242525 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2526 let current_index = <ItemListIndex>::get(collection_id)2527 .checked_add(1)2528 .unwrap();25292530 Self::add_token_index(collection_id, current_index, owner).unwrap();25312532 <ItemListIndex>::insert(collection_id, current_index);25332534 2535 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2536 .checked_add(item.value)2537 .unwrap();2538 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2539 }25402541 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2542 let current_index = <ItemListIndex>::get(collection_id)2543 .checked_add(1)2544 .unwrap();25452546 let value = item.owner.first().unwrap().fraction;2547 let owner = item.owner.first().unwrap().owner.clone();25482549 Self::add_token_index(collection_id, current_index, &owner).unwrap();25502551 <ItemListIndex>::insert(collection_id, current_index);25522553 2554 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2555 .checked_add(value)2556 .unwrap();2557 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2558 }25592560 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2561 2562 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {25632564 2565 let count = <AccountItemCount<T>>::get(owner.as_sub());2566 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25672568 <AccountItemCount<T>>::insert(owner.as_sub(), count2569 .checked_add(1)2570 .ok_or(Error::<T>::NumOverflow)?);2571 }2572 else {2573 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2574 }25752576 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2577 if list_exists {2578 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2579 let item_contains = list.contains(&item_index.clone());25802581 if !item_contains {2582 list.push(item_index.clone());2583 }25842585 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2586 } else {2587 let mut itm = Vec::new();2588 itm.push(item_index.clone());2589 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2590 }25912592 Ok(())2593 }25942595 fn remove_token_index(2596 collection_id: CollectionId,2597 item_index: TokenId,2598 owner: &T::CrossAccountId,2599 ) -> DispatchResult {26002601 2602 <AccountItemCount<T>>::insert(owner.as_sub(), 2603 <AccountItemCount<T>>::get(owner.as_sub())2604 .checked_sub(1)2605 .ok_or(Error::<T>::NumOverflow)?);260626072608 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2609 if list_exists {2610 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2611 let item_contains = list.contains(&item_index.clone());26122613 if item_contains {2614 list.retain(|&item| item != item_index);2615 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2616 }2617 }26182619 Ok(())2620 }26212622 fn move_token_index(2623 collection_id: CollectionId,2624 item_index: TokenId,2625 old_owner: &T::CrossAccountId,2626 new_owner: &T::CrossAccountId,2627 ) -> DispatchResult {2628 Self::remove_token_index(collection_id, item_index, old_owner)?;2629 Self::add_token_index(collection_id, item_index, new_owner)?;26302631 Ok(())2632 }2633 2634 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2635 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26362637 Ok(())2638 }2639}2640264126422643264426452646pub type Multiplier = FixedU128;26472648type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2649265026512652#[derive(Encode, Decode, Clone, Eq, PartialEq)]2653pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26542655impl<T: Config + Send + Sync> sp_std::fmt::Debug 2656 for ChargeTransactionPayment<T>2657{2658 #[cfg(feature = "std")]2659 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2660 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2661 }2662 #[cfg(not(feature = "std"))]2663 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2664 Ok(())2665 }2666}26672668impl<T: Config> ChargeTransactionPayment<T>2669where2670 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2671 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2672 T::AccountId: AsRef<[u8]>,2673 T::AccountId: UncheckedFrom<T::Hash>,2674{2675 fn traditional_fee(2676 len: usize,2677 info: &DispatchInfoOf<T::Call>,2678 tip: BalanceOf<T>,2679 ) -> BalanceOf<T>2680 where2681 T::Call: Dispatchable<Info = DispatchInfo>,2682 {2683 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2684 }26852686 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2687 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2688 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2689 let len_saturation = max_block_length as u64 / (len as u64).max(1);2690 let coefficient: BalanceOf<T> = weight_saturation2691 .min(len_saturation)2692 .saturated_into::<BalanceOf<T>>();2693 final_fee2694 .saturating_mul(coefficient)2695 .saturated_into::<TransactionPriority>()2696 }26972698 fn withdraw_fee(2699 &self,2700 who: &T::AccountId,2701 call: &T::Call,2702 info: &DispatchInfoOf<T::Call>,2703 len: usize,2704 ) -> Result<2705 (2706 BalanceOf<T>,2707 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2708 ),2709 TransactionValidityError,2710 > {2711 let tip = self.0;27122713 let fee = Self::traditional_fee(len, info, tip);27142715 2716 if fee.is_zero() {2717 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2718 .map(|i| (fee, i));2719 }27202721 2722 2723 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2724 Some(Call::create_item(collection_id, _owner, _properties)) => {2725 let collection = <CollectionById<T>>::get(collection_id)?;27262727 2728 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27292730 let limit = collection.limits.sponsor_transfer_timeout;2731 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2732 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2733 let limit_time = last_tx_block + limit.into();2734 if block_number <= limit_time {2735 return None;2736 }2737 }2738 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27392740 2741 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2742 collection.sponsorship.sponsor()2743 .cloned()2744 } else {2745 None2746 }2747 }2748 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2749 let collection = <CollectionById<T>>::get(collection_id)?;2750 2751 let mut sponsor_transfer = false;2752 if collection.sponsorship.confirmed() {27532754 let collection_limits = collection.limits;2755 let collection_mode = collection.mode;2756 2757 2758 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2759 sponsor_transfer = match collection_mode {2760 CollectionMode::NFT => {2761 2762 2763 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2764 collection_limits.sponsor_transfer_timeout2765 } else {2766 ChainLimit::get().nft_sponsor_transfer_timeout2767 };2768 2769 let mut sponsored = true;2770 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2771 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2772 let limit_time = last_tx_block + limit.into();2773 if block_number <= limit_time {2774 sponsored = false;2775 }2776 }2777 if sponsored {2778 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2779 }27802781 sponsored2782 }2783 CollectionMode::Fungible(_) => {2784 2785 2786 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2787 collection_limits.sponsor_transfer_timeout2788 } else {2789 ChainLimit::get().fungible_sponsor_transfer_timeout2790 };2791 2792 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2793 let mut sponsored = true;2794 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2795 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2796 let limit_time = last_tx_block + limit.into();2797 if block_number <= limit_time {2798 sponsored = false;2799 }2800 }2801 if sponsored {2802 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2803 }28042805 sponsored2806 }2807 CollectionMode::ReFungible => {2808 2809 2810 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2811 collection_limits.sponsor_transfer_timeout2812 } else {2813 ChainLimit::get().refungible_sponsor_transfer_timeout2814 };2815 2816 let mut sponsored = true;2817 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2818 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2819 let limit_time = last_tx_block + limit.into();2820 if block_number <= limit_time {2821 sponsored = false;2822 }2823 }2824 if sponsored {2825 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2826 }28272828 sponsored2829 }2830 _ => {2831 false2832 },2833 };2834 }28352836 if !sponsor_transfer {2837 None2838 } else {2839 collection.sponsorship.sponsor()2840 .cloned()2841 }2842 }28432844 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2845 let mut sponsor_metadata_changes = false;28462847 let collection = <CollectionById<T>>::get(collection_id)?;28482849 if2850 collection.sponsorship.confirmed() &&2851 2852 2853 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2854 data.len() <= collection.limits.sponsored_data_size as usize2855 {2856 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2857 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28582859 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2860 .map(|last_block| block_number - last_block > rate_limit)2861 .unwrap_or(true) 2862 {2863 sponsor_metadata_changes = true;2864 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2865 }2866 }2867 }28682869 if !sponsor_metadata_changes {2870 None2871 } else {2872 collection.sponsorship.sponsor().cloned()2873 }2874 }28752876 _ => None,2877 })();28782879 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2880 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28812882 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28832884 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2885 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2886 2887 if !owned_contract && white_list_enabled {2888 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2889 return Err(InvalidTransaction::Call.into());2890 }2891 }2892 },2893 _ => {},2894 }28952896 2897 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {28982899 2900 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29012902 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2903 &who,2904 code_hash,2905 salt,2906 );2907 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29082909 None2910 },29112912 2913 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29142915 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2916 &who,2917 &T::Hashing::hash(&_code),2918 _salt,2919 );29202921 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29222923 None2924 }29252926 2927 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29282929 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29302931 let mut sponsor_transfer = false;2932 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2933 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2934 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2935 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2936 let limit_time = last_tx_block + rate_limit;29372938 if block_number >= limit_time {2939 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2940 sponsor_transfer = true;2941 }2942 } else {2943 sponsor_transfer = false;2944 }2945 2946 if sponsor_transfer {2947 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2948 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2949 return Some(called_contract);2950 }2951 }2952 }29532954 None2955 },29562957 _ => None,2958 });29592960 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29612962 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2963 .map(|i| (fee, i))2964 }2965}296629672968impl<T: Config + Send + Sync> SignedExtension2969 for ChargeTransactionPayment<T>2970where2971 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2972 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2973 T::AccountId: AsRef<[u8]>,2974 T::AccountId: UncheckedFrom<T::Hash>,2975{2976 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2977 type AccountId = T::AccountId;2978 type Call = T::Call;2979 type AdditionalSigned = ();2980 type Pre = (2981 2982 BalanceOf<T>,2983 2984 Self::AccountId,2985 2986 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2987 );2988 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2989 Ok(())2990 }29912992 fn validate(2993 &self,2994 who: &Self::AccountId,2995 call: &Self::Call,2996 info: &DispatchInfoOf<Self::Call>,2997 len: usize,2998 ) -> TransactionValidity {2999 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3000 Ok(ValidTransaction {3001 priority: Self::get_priority(len, info, fee),3002 ..Default::default()3003 })3004 }30053006 fn pre_dispatch(3007 self,3008 who: &Self::AccountId,3009 call: &Self::Call,3010 info: &DispatchInfoOf<Self::Call>,3011 len: usize,3012 ) -> Result<Self::Pre, TransactionValidityError> {3013 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3014 Ok((self.0, who.clone(), imbalance))3015 }30163017 fn post_dispatch(3018 pre: Self::Pre,3019 info: &DispatchInfoOf<Self::Call>,3020 post_info: &PostDispatchInfoOf<Self::Call>,3021 len: usize,3022 _result: &DispatchResult,3023 ) -> Result<(), TransactionValidityError> {3024 let (tip, who, imbalance) = pre;3025 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3026 len as u32,3027 info,3028 post_info,3029 tip,3030 );3031 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3032 Ok(())3033 }3034}3035303630373038sp_api::decl_runtime_apis! {3039 pub trait NftApi {3040 3041 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3042 }3043}