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 = T::CrossAccountId::from_sub(ensure_signed(origin)?);1343 1344 let target_collection = Self::get_collection(collection_id)?;1345 Self::set_variable_meta_data_internal(sender, &target_collection, item_id, data)?;13461347 Ok(())1348 }1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 #[weight = <T as Config>::WeightInfo::set_schema_version()]1365 #[transactional]1366 pub fn set_schema_version(1367 origin,1368 collection_id: CollectionId,1369 version: SchemaVersion1370 ) -> DispatchResult {1371 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1372 let mut target_collection = Self::get_collection(collection_id)?;1373 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1374 target_collection.schema_version = version;1375 Self::save_collection(target_collection);13761377 Ok(())1378 }13791380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1393 #[transactional]1394 pub fn set_offchain_schema(1395 origin,1396 collection_id: CollectionId,1397 schema: Vec<u8>1398 ) -> DispatchResult {1399 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1400 let mut target_collection = Self::get_collection(collection_id)?;1401 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14021403 1404 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14051406 target_collection.offchain_schema = schema;1407 Self::save_collection(target_collection);14081409 Ok(())1410 }14111412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1425 #[transactional]1426 pub fn set_const_on_chain_schema (1427 origin,1428 collection_id: CollectionId,1429 schema: Vec<u8>1430 ) -> DispatchResult {1431 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1432 let mut target_collection = Self::get_collection(collection_id)?;1433 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14341435 1436 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14371438 target_collection.const_on_chain_schema = schema;1439 Self::save_collection(target_collection);14401441 Ok(())1442 }14431444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1457 #[transactional]1458 pub fn set_variable_on_chain_schema (1459 origin,1460 collection_id: CollectionId,1461 schema: Vec<u8>1462 ) -> DispatchResult {1463 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1464 let mut target_collection = Self::get_collection(collection_id)?;1465 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14661467 1468 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14691470 target_collection.variable_on_chain_schema = schema;1471 Self::save_collection(target_collection);14721473 Ok(())1474 }14751476 1477 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1478 #[transactional]1479 pub fn set_chain_limits(1480 origin,1481 limits: ChainLimits1482 ) -> DispatchResult {14831484 #[cfg(not(feature = "runtime-benchmarks"))]1485 ensure_root(origin)?;14861487 <ChainLimit>::put(limits);1488 Ok(())1489 }14901491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1503 #[transactional]1504 pub fn enable_contract_sponsoring(1505 origin,1506 contract_address: T::AccountId,1507 enable: bool1508 ) -> DispatchResult {15091510 let sender = ensure_signed(origin)?;15111512 #[cfg(feature = "runtime-benchmarks")]1513 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15141515 Self::ensure_contract_owned(sender, &contract_address)?;15161517 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1518 Ok(())1519 }15201521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1539 #[transactional]1540 pub fn set_contract_sponsoring_rate_limit(1541 origin,1542 contract_address: T::AccountId,1543 rate_limit: T::BlockNumber1544 ) -> DispatchResult {1545 let sender = ensure_signed(origin)?;15461547 #[cfg(feature = "runtime-benchmarks")]1548 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15491550 Self::ensure_contract_owned(sender, &contract_address)?;1551 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1552 Ok(())1553 }15541555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1567 #[transactional]1568 pub fn toggle_contract_white_list(1569 origin,1570 contract_address: T::AccountId,1571 enable: bool1572 ) -> DispatchResult {1573 let sender = ensure_signed(origin)?;15741575 #[cfg(feature = "runtime-benchmarks")]1576 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15771578 Self::ensure_contract_owned(sender, &contract_address)?;1579 if enable {1580 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1581 } else {1582 <ContractWhiteListEnabled<T>>::remove(contract_address);1583 }1584 Ok(())1585 }1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1599 #[transactional]1600 pub fn add_to_contract_white_list(1601 origin,1602 contract_address: T::AccountId,1603 account_address: T::AccountId1604 ) -> DispatchResult {1605 let sender = ensure_signed(origin)?;16061607 #[cfg(feature = "runtime-benchmarks")]1608 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1609 1610 Self::ensure_contract_owned(sender, &contract_address)?; 1611 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1612 Ok(())1613 }16141615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1627 #[transactional]1628 pub fn remove_from_contract_white_list(1629 origin,1630 contract_address: T::AccountId,1631 account_address: T::AccountId1632 ) -> DispatchResult {1633 let sender = ensure_signed(origin)?;16341635 #[cfg(feature = "runtime-benchmarks")]1636 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16371638 Self::ensure_contract_owned(sender, &contract_address)?;1639 <ContractWhiteList<T>>::remove(contract_address, account_address);1640 Ok(())1641 }16421643 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1644 #[transactional]1645 pub fn set_collection_limits(1646 origin,1647 collection_id: u32,1648 new_limits: CollectionLimits<T::BlockNumber>,1649 ) -> DispatchResult {1650 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1651 let mut target_collection = Self::get_collection(collection_id)?;1652 Self::check_owner_permissions(&target_collection, &sender)?;1653 let old_limits = &target_collection.limits;1654 let chain_limits = ChainLimit::get();16551656 1657 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1658 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1659 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1660 Error::<T>::CollectionLimitBoundsExceeded);16611662 1663 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1664 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16651666 ensure!(1667 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1668 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1669 Error::<T>::OwnerPermissionsCantBeReverted,1670 );16711672 target_collection.limits = new_limits;1673 Self::save_collection(target_collection);16741675 Ok(())1676 } 1677 }1678}16791680impl<T: Config> Module<T> {16811682 pub fn transfer_internal(sender: T::CrossAccountId, recipient: T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1683 1684 Self::is_correct_transfer(target_collection, &recipient)?;16851686 1687 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1688 Self::is_owner_or_admin_permissions(target_collection, &sender),1689 Error::<T>::NoPermission);16901691 if target_collection.access == AccessMode::WhiteList {1692 Self::check_white_list(target_collection, &sender)?;1693 Self::check_white_list(target_collection, &recipient)?;1694 }16951696 match target_collection.mode1697 {1698 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1699 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1700 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1701 _ => ()1702 };17031704 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17051706 Ok(())1707 }17081709 pub fn approve_internal(1710 sender: T::CrossAccountId,1711 spender: T::CrossAccountId,1712 collection: &CollectionHandle<T>,1713 item_id: TokenId,1714 amount: u1281715 ) -> DispatchResult {1716 Self::token_exists(&collection, item_id)?;17171718 1719 let bypasses_limits = collection.limits.owner_can_transfer &&1720 Self::is_owner_or_admin_permissions(1721 &collection,1722 &sender,1723 );17241725 let allowance_limit = if bypasses_limits {1726 None1727 } else if let Some(amount) = Self::owned_amount(1728 &sender,1729 &collection,1730 item_id,1731 ) {1732 Some(amount)1733 } else {1734 fail!(Error::<T>::NoPermission);1735 };17361737 if collection.access == AccessMode::WhiteList {1738 Self::check_white_list(&collection, &sender)?;1739 Self::check_white_list(&collection, &spender)?;1740 }17411742 let allowance: u128 = amount1743 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1744 .ok_or(Error::<T>::NumOverflow)?;1745 if let Some(limit) = allowance_limit {1746 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1747 }1748 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17491750 if matches!(collection.mode, CollectionMode::NFT) {1751 1752 collection.log(1753 Vec::from([1754 eth::APPROVAL_NFT_TOPIC,1755 eth::address_to_topic(sender.as_eth()),1756 eth::address_to_topic(spender.as_eth()),1757 ]),1758 abi_encode!(uint256(item_id.into())),1759 );1760 }17611762 if matches!(collection.mode, CollectionMode::Fungible(_)) {1763 1764 collection.log(1765 Vec::from([1766 eth::APPROVAL_FUNGIBLE_TOPIC,1767 eth::address_to_topic(sender.as_eth()),1768 eth::address_to_topic(spender.as_eth()),1769 ]),1770 abi_encode!(uint256(allowance.into())),1771 );1772 }17731774 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1775 Ok(())1776 }17771778 pub fn transfer_from_internal(1779 sender: T::CrossAccountId,1780 from: T::CrossAccountId,1781 recipient: T::CrossAccountId,1782 collection: &CollectionHandle<T>,1783 item_id: TokenId,1784 amount: u128,1785 ) -> DispatchResult {1786 1787 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17881789 1790 Self::is_correct_transfer(&collection, &recipient)?;17911792 1793 ensure!(1794 approval >= amount || 1795 (1796 collection.limits.owner_can_transfer &&1797 Self::is_owner_or_admin_permissions(&collection, &sender)1798 ),1799 Error::<T>::NoPermission1800 );18011802 if collection.access == AccessMode::WhiteList {1803 Self::check_white_list(&collection, &sender)?;1804 Self::check_white_list(&collection, &recipient)?;1805 }18061807 1808 let allowance = approval.saturating_sub(amount);1809 if allowance > 0 {1810 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1811 } else {1812 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1813 }18141815 match collection.mode {1816 CollectionMode::NFT => {1817 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1818 }1819 CollectionMode::Fungible(_) => {1820 Self::transfer_fungible(&collection, amount, &from, &recipient)?1821 }1822 CollectionMode::ReFungible => {1823 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1824 }1825 _ => ()1826 };18271828 if matches!(collection.mode, CollectionMode::Fungible(_)) {1829 collection.log(1830 Vec::from([1831 eth::APPROVAL_FUNGIBLE_TOPIC,1832 eth::address_to_topic(from.as_eth()),1833 eth::address_to_topic(sender.as_eth()),1834 ]),1835 abi_encode!(uint256(allowance.into())),1836 );1837 }18381839 Ok(())1840 }18411842 pub fn set_variable_meta_data_internal(1843 sender: T::CrossAccountId,1844 collection: &CollectionHandle<T>, 1845 item_id: TokenId,1846 data: Vec<u8>,1847 ) -> DispatchResult {1848 Self::token_exists(&collection, item_id)?;18491850 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18511852 1853 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1854 Self::is_owner_or_admin_permissions(&collection, &sender),1855 Error::<T>::NoPermission);18561857 match collection.mode1858 {1859 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1860 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1861 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1862 _ => fail!(Error::<T>::UnexpectedCollectionType)1863 };18641865 Ok(())1866 }18671868 pub fn create_multiple_items_internal(1869 sender: T::CrossAccountId,1870 collection: &CollectionHandle<T>,1871 owner: T::CrossAccountId,1872 items_data: Vec<CreateItemData>,1873 ) -> DispatchResult {1874 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18751876 for data in &items_data {1877 Self::validate_create_item_args(&collection, data)?;1878 }1879 for data in &items_data {1880 Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1881 }18821883 Ok(())1884 }18851886 pub fn burn_item_internal(1887 sender: &T::CrossAccountId,1888 collection: &CollectionHandle<T>,1889 item_id: TokenId,1890 value: u128,1891 ) -> DispatchResult {1892 ensure!(1893 Self::is_item_owner(&sender, &collection, item_id) ||1894 (1895 collection.limits.owner_can_transfer &&1896 Self::is_owner_or_admin_permissions(&collection, &sender)1897 ),1898 Error::<T>::NoPermission1899 );19001901 if collection.access == AccessMode::WhiteList {1902 Self::check_white_list(&collection, &sender)?;1903 }19041905 match collection.mode1906 {1907 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1908 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1909 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1910 _ => ()1911 };19121913 Ok(())1914 }19151916 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1917 let collection_id = collection.id;19181919 1920 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1921 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1922 1923 Ok(())1924 }19251926 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1927 let collection_id = collection.id;19281929 1930 let total_items: u32 = ItemListIndex::get(collection_id)1931 .checked_add(amount)1932 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1933 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1934 .checked_add(amount)1935 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1936 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1937 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19381939 if !Self::is_owner_or_admin_permissions(collection, &sender) {1940 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1941 Self::check_white_list(collection, owner)?;1942 Self::check_white_list(collection, sender)?;1943 }19441945 Ok(())1946 }19471948 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1949 match target_collection.mode1950 {1951 CollectionMode::NFT => {1952 if let CreateItemData::NFT(data) = data {1953 1954 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1955 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1956 } else {1957 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1958 }1959 },1960 CollectionMode::Fungible(_) => {1961 if let CreateItemData::Fungible(_) = data {1962 } else {1963 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1964 }1965 },1966 CollectionMode::ReFungible => {1967 if let CreateItemData::ReFungible(data) = data {19681969 1970 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1971 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19721973 1974 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1975 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1976 } else {1977 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1978 }1979 },1980 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1981 };19821983 Ok(())1984 }19851986 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1987 match data1988 {1989 CreateItemData::NFT(data) => {1990 let item = NftItemType {1991 owner: owner.clone(),1992 const_data: data.const_data,1993 variable_data: data.variable_data1994 };19951996 Self::add_nft_item(collection, item)?;1997 },1998 CreateItemData::Fungible(data) => {1999 Self::add_fungible_item(collection, &owner, data.value)?;2000 },2001 CreateItemData::ReFungible(data) => {2002 let mut owner_list = Vec::new();2003 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20042005 let item = ReFungibleItemType {2006 owner: owner_list,2007 const_data: data.const_data,2008 variable_data: data.variable_data2009 };20102011 Self::add_refungible_item(collection, item)?;2012 }2013 };20142015 Ok(())2016 }20172018 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2019 let collection_id = collection.id;20202021 2022 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20232024 2025 let item = FungibleItemType {2026 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2027 };2028 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20292030 2031 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2032 .checked_add(value)2033 .ok_or(Error::<T>::NumOverflow)?;2034 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20352036 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2037 Ok(())2038 }20392040 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2041 let collection_id = collection.id;20422043 let current_index = <ItemListIndex>::get(collection_id)2044 .checked_add(1)2045 .ok_or(Error::<T>::NumOverflow)?;2046 let itemcopy = item.clone();20472048 ensure!(2049 item.owner.len() == 1,2050 Error::<T>::BadCreateRefungibleCall,2051 );2052 let item_owner = item.owner.first().expect("only one owner is defined");20532054 let value = item_owner.fraction;2055 let owner = item_owner.owner.clone();20562057 Self::add_token_index(collection_id, current_index, &owner)?;20582059 <ItemListIndex>::insert(collection_id, current_index);2060 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20612062 2063 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2064 .checked_add(value)2065 .ok_or(Error::<T>::NumOverflow)?;2066 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20672068 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2069 Ok(())2070 }20712072 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2073 let collection_id = collection.id;20742075 let current_index = <ItemListIndex>::get(collection_id)2076 .checked_add(1)2077 .ok_or(Error::<T>::NumOverflow)?;20782079 let item_owner = item.owner.clone();2080 Self::add_token_index(collection_id, current_index, &item.owner)?;20812082 <ItemListIndex>::insert(collection_id, current_index);2083 <NftItemList<T>>::insert(collection_id, current_index, item);20842085 2086 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2087 .checked_add(1)2088 .ok_or(Error::<T>::NumOverflow)?;2089 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);20902091 collection.log(2092 Vec::from([2093 eth::TRANSFER_NFT_TOPIC,2094 eth::address_to_topic(&H160::default()),2095 eth::address_to_topic(item_owner.as_eth()),2096 ]),2097 abi_encode!(uint256(current_index.into())),2098 );2099 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2100 Ok(())2101 }21022103 fn burn_refungible_item(2104 collection: &CollectionHandle<T>,2105 item_id: TokenId,2106 owner: &T::CrossAccountId,2107 ) -> DispatchResult {2108 let collection_id = collection.id;21092110 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2111 .ok_or(Error::<T>::TokenNotFound)?;2112 let rft_balance = token2113 .owner2114 .iter()2115 .find(|&i| i.owner == *owner)2116 .ok_or(Error::<T>::TokenNotFound)?;2117 Self::remove_token_index(collection_id, item_id, owner)?;21182119 2120 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2121 .checked_sub(rft_balance.fraction)2122 .ok_or(Error::<T>::NumOverflow)?;2123 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21242125 2126 let index = token2127 .owner2128 .iter()2129 .position(|i| i.owner == *owner)2130 .expect("owned item is exists");2131 token.owner.remove(index);2132 let owner_count = token.owner.len();21332134 2135 if owner_count == 0 {2136 <ReFungibleItemList<T>>::remove(collection_id, item_id);2137 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2138 }2139 else {2140 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2141 }21422143 Ok(())2144 }21452146 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2147 let collection_id = collection.id;21482149 let item = <NftItemList<T>>::get(collection_id, item_id)2150 .ok_or(Error::<T>::TokenNotFound)?;2151 Self::remove_token_index(collection_id, item_id, &item.owner)?;21522153 2154 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2155 .checked_sub(1)2156 .ok_or(Error::<T>::NumOverflow)?;2157 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2158 <NftItemList<T>>::remove(collection_id, item_id);2159 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21602161 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2162 Ok(())2163 }21642165 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2166 let collection_id = collection.id;21672168 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2169 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21702171 2172 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2173 .checked_sub(value)2174 .ok_or(Error::<T>::NumOverflow)?;2175 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21762177 if balance.value - value > 0 {2178 balance.value -= value;2179 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2180 }2181 else {2182 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2183 }21842185 collection.log(2186 Vec::from([2187 eth::TRANSFER_FUNGIBLE_TOPIC,2188 eth::address_to_topic(owner.as_eth()),2189 eth::address_to_topic(&H160::default()),2190 ]),2191 abi_encode!(uint256(value.into())),2192 );2193 Ok(())2194 }21952196 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2197 Ok(<CollectionHandle<T>>::get(collection_id)2198 .ok_or(Error::<T>::CollectionNotFound)?)2199 }22002201 fn save_collection(collection: CollectionHandle<T>) {2202 <CollectionById<T>>::insert(collection.id, collection.into_inner());2203 }22042205 fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2206 T::EthereumTransactionSender::submit_logs_transaction(2207 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2208 collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2209 )2210 }22112212 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2213 ensure!(2214 *subject == target_collection.owner,2215 Error::<T>::NoPermission2216 );22172218 Ok(())2219 }22202221 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2222 *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2223 }22242225 fn check_owner_or_admin_permissions(2226 collection: &CollectionHandle<T>,2227 subject: &T::CrossAccountId,2228 ) -> DispatchResult {2229 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22302231 Ok(())2232 }22332234 fn owned_amount(2235 subject: &T::CrossAccountId,2236 target_collection: &CollectionHandle<T>,2237 item_id: TokenId,2238 ) -> Option<u128> {2239 let collection_id = target_collection.id;22402241 match target_collection.mode {2242 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2243 .then(|| 1),2244 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2245 .value),2246 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2247 .owner2248 .iter()2249 .find(|i| i.owner == *subject)2250 .map(|i| i.fraction),2251 CollectionMode::Invalid => None,2252 }2253 }22542255 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2256 match target_collection.mode {2257 CollectionMode::Fungible(_) => true,2258 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2259 }2260 }22612262 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2263 let collection_id = collection.id;22642265 let mes = Error::<T>::AddresNotInWhiteList;2266 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22672268 Ok(())2269 }22702271 2272 2273 fn token_exists(2274 target_collection: &CollectionHandle<T>,2275 item_id: TokenId,2276 ) -> DispatchResult {2277 let collection_id = target_collection.id;2278 let exists = match target_collection.mode2279 {2280 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2281 CollectionMode::Fungible(_) => true,2282 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2283 _ => false2284 };22852286 ensure!(exists == true, Error::<T>::TokenNotFound);2287 Ok(())2288 }22892290 fn transfer_fungible(2291 collection: &CollectionHandle<T>,2292 value: u128,2293 owner: &T::CrossAccountId,2294 recipient: &T::CrossAccountId,2295 ) -> DispatchResult {2296 let collection_id = collection.id;22972298 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2299 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23002301 2302 Self::add_fungible_item(collection, recipient, value)?;23032304 2305 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23062307 2308 if balance.value == value {2309 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2310 }2311 else {2312 balance.value -= value;2313 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2314 }23152316 collection.log(2317 Vec::from([2318 eth::TRANSFER_FUNGIBLE_TOPIC,2319 eth::address_to_topic(owner.as_eth()),2320 eth::address_to_topic(recipient.as_eth()),2321 ]),2322 abi_encode!(uint256(value.into())),2323 );2324 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23252326 Ok(())2327 }23282329 fn transfer_refungible(2330 collection: &CollectionHandle<T>,2331 item_id: TokenId,2332 value: u128,2333 owner: T::CrossAccountId,2334 new_owner: T::CrossAccountId,2335 ) -> DispatchResult {2336 let collection_id = collection.id;2337 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2338 .ok_or(Error::<T>::TokenNotFound)?;23392340 let item = full_item2341 .owner2342 .iter()2343 .filter(|i| i.owner == owner)2344 .next()2345 .ok_or(Error::<T>::TokenNotFound)?;2346 let amount = item.fraction;23472348 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23492350 2351 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2352 .checked_sub(value)2353 .ok_or(Error::<T>::NumOverflow)?;2354 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23552356 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2357 .checked_add(value)2358 .ok_or(Error::<T>::NumOverflow)?;2359 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23602361 let old_owner = item.owner.clone();2362 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23632364 2365 if amount == value && !new_owner_has_account {2366 2367 2368 let mut new_full_item = full_item.clone();2369 new_full_item2370 .owner2371 .iter_mut()2372 .find(|i| i.owner == owner)2373 .expect("old owner does present in refungible")2374 .owner = new_owner.clone();2375 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23762377 2378 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2379 } else {2380 let mut new_full_item = full_item.clone();2381 new_full_item2382 .owner2383 .iter_mut()2384 .find(|i| i.owner == owner)2385 .expect("old owner does present in refungible")2386 .fraction -= value;23872388 2389 if new_owner_has_account {2390 2391 new_full_item2392 .owner2393 .iter_mut()2394 .find(|i| i.owner == new_owner)2395 .expect("new owner has account")2396 .fraction += value;2397 } else {2398 2399 new_full_item.owner.push(Ownership {2400 owner: new_owner.clone(),2401 fraction: value,2402 });2403 Self::add_token_index(collection_id, item_id, &new_owner)?;2404 }24052406 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2407 }24082409 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24102411 Ok(())2412 }24132414 fn transfer_nft(2415 collection: &CollectionHandle<T>,2416 item_id: TokenId,2417 sender: T::CrossAccountId,2418 new_owner: T::CrossAccountId,2419 ) -> DispatchResult {2420 let collection_id = collection.id;2421 let mut item = <NftItemList<T>>::get(collection_id, item_id)2422 .ok_or(Error::<T>::TokenNotFound)?;24232424 ensure!(2425 sender == item.owner,2426 Error::<T>::MustBeTokenOwner2427 );24282429 2430 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2431 .checked_sub(1)2432 .ok_or(Error::<T>::NumOverflow)?;2433 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24342435 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2436 .checked_add(1)2437 .ok_or(Error::<T>::NumOverflow)?;2438 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24392440 2441 let old_owner = item.owner.clone();2442 item.owner = new_owner.clone();2443 <NftItemList<T>>::insert(collection_id, item_id, item);24442445 2446 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24472448 collection.log(2449 Vec::from([2450 eth::TRANSFER_NFT_TOPIC,2451 eth::address_to_topic(sender.as_eth()),2452 eth::address_to_topic(new_owner.as_eth()),2453 ]),2454 abi_encode!(uint256(item_id.into())),2455 );2456 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24572458 Ok(())2459 }2460 2461 fn set_re_fungible_variable_data(2462 collection: &CollectionHandle<T>,2463 item_id: TokenId,2464 data: Vec<u8>2465 ) -> DispatchResult {2466 let collection_id = collection.id;2467 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2468 .ok_or(Error::<T>::TokenNotFound)?;24692470 item.variable_data = data;24712472 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24732474 Ok(())2475 }24762477 fn set_nft_variable_data(2478 collection: &CollectionHandle<T>,2479 item_id: TokenId,2480 data: Vec<u8>2481 ) -> DispatchResult {2482 let collection_id = collection.id;2483 let mut item = <NftItemList<T>>::get(collection_id, item_id)2484 .ok_or(Error::<T>::TokenNotFound)?;2485 2486 item.variable_data = data;24872488 <NftItemList<T>>::insert(collection_id, item_id, item);2489 2490 Ok(())2491 }24922493 fn init_collection(item: &Collection<T>) {2494 2495 assert!(2496 item.decimal_points <= MAX_DECIMAL_POINTS,2497 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2498 );2499 assert!(2500 item.name.len() <= 64,2501 "Collection name can not be longer than 63 char"2502 );2503 assert!(2504 item.name.len() <= 256,2505 "Collection description can not be longer than 255 char"2506 );2507 assert!(2508 item.token_prefix.len() <= 16,2509 "Token prefix can not be longer than 15 char"2510 );25112512 2513 let next_id = CreatedCollectionCount::get()2514 .checked_add(1)2515 .unwrap();25162517 CreatedCollectionCount::put(next_id);2518 }25192520 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2521 let current_index = <ItemListIndex>::get(collection_id)2522 .checked_add(1)2523 .unwrap();25242525 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25262527 <ItemListIndex>::insert(collection_id, current_index);25282529 2530 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2531 .checked_add(1)2532 .unwrap();2533 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2534 }25352536 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2537 let current_index = <ItemListIndex>::get(collection_id)2538 .checked_add(1)2539 .unwrap();25402541 Self::add_token_index(collection_id, current_index, owner).unwrap();25422543 <ItemListIndex>::insert(collection_id, current_index);25442545 2546 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2547 .checked_add(item.value)2548 .unwrap();2549 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2550 }25512552 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2553 let current_index = <ItemListIndex>::get(collection_id)2554 .checked_add(1)2555 .unwrap();25562557 let value = item.owner.first().unwrap().fraction;2558 let owner = item.owner.first().unwrap().owner.clone();25592560 Self::add_token_index(collection_id, current_index, &owner).unwrap();25612562 <ItemListIndex>::insert(collection_id, current_index);25632564 2565 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2566 .checked_add(value)2567 .unwrap();2568 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2569 }25702571 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2572 2573 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {25742575 2576 let count = <AccountItemCount<T>>::get(owner.as_sub());2577 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25782579 <AccountItemCount<T>>::insert(owner.as_sub(), count2580 .checked_add(1)2581 .ok_or(Error::<T>::NumOverflow)?);2582 }2583 else {2584 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2585 }25862587 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2588 if list_exists {2589 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2590 let item_contains = list.contains(&item_index.clone());25912592 if !item_contains {2593 list.push(item_index.clone());2594 }25952596 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2597 } else {2598 let mut itm = Vec::new();2599 itm.push(item_index.clone());2600 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2601 }26022603 Ok(())2604 }26052606 fn remove_token_index(2607 collection_id: CollectionId,2608 item_index: TokenId,2609 owner: &T::CrossAccountId,2610 ) -> DispatchResult {26112612 2613 <AccountItemCount<T>>::insert(owner.as_sub(), 2614 <AccountItemCount<T>>::get(owner.as_sub())2615 .checked_sub(1)2616 .ok_or(Error::<T>::NumOverflow)?);261726182619 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2620 if list_exists {2621 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2622 let item_contains = list.contains(&item_index.clone());26232624 if item_contains {2625 list.retain(|&item| item != item_index);2626 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2627 }2628 }26292630 Ok(())2631 }26322633 fn move_token_index(2634 collection_id: CollectionId,2635 item_index: TokenId,2636 old_owner: &T::CrossAccountId,2637 new_owner: &T::CrossAccountId,2638 ) -> DispatchResult {2639 Self::remove_token_index(collection_id, item_index, old_owner)?;2640 Self::add_token_index(collection_id, item_index, new_owner)?;26412642 Ok(())2643 }2644 2645 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2646 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26472648 Ok(())2649 }2650}2651265226532654265526562657pub type Multiplier = FixedU128;26582659type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2660266126622663#[derive(Encode, Decode, Clone, Eq, PartialEq)]2664pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26652666impl<T: Config + Send + Sync> sp_std::fmt::Debug 2667 for ChargeTransactionPayment<T>2668{2669 #[cfg(feature = "std")]2670 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2671 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2672 }2673 #[cfg(not(feature = "std"))]2674 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2675 Ok(())2676 }2677}26782679impl<T: Config> ChargeTransactionPayment<T>2680where2681 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2682 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2683 T::AccountId: AsRef<[u8]>,2684 T::AccountId: UncheckedFrom<T::Hash>,2685{2686 fn traditional_fee(2687 len: usize,2688 info: &DispatchInfoOf<T::Call>,2689 tip: BalanceOf<T>,2690 ) -> BalanceOf<T>2691 where2692 T::Call: Dispatchable<Info = DispatchInfo>,2693 {2694 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2695 }26962697 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2698 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2699 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2700 let len_saturation = max_block_length as u64 / (len as u64).max(1);2701 let coefficient: BalanceOf<T> = weight_saturation2702 .min(len_saturation)2703 .saturated_into::<BalanceOf<T>>();2704 final_fee2705 .saturating_mul(coefficient)2706 .saturated_into::<TransactionPriority>()2707 }27082709 fn withdraw_fee(2710 &self,2711 who: &T::AccountId,2712 call: &T::Call,2713 info: &DispatchInfoOf<T::Call>,2714 len: usize,2715 ) -> Result<2716 (2717 BalanceOf<T>,2718 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2719 ),2720 TransactionValidityError,2721 > {2722 let tip = self.0;27232724 let fee = Self::traditional_fee(len, info, tip);27252726 2727 if fee.is_zero() {2728 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2729 .map(|i| (fee, i));2730 }27312732 2733 2734 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2735 Some(Call::create_item(collection_id, _owner, _properties)) => {2736 let collection = <CollectionById<T>>::get(collection_id)?;27372738 2739 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27402741 let limit = collection.limits.sponsor_transfer_timeout;2742 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2743 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2744 let limit_time = last_tx_block + limit.into();2745 if block_number <= limit_time {2746 return None;2747 }2748 }2749 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27502751 2752 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2753 collection.sponsorship.sponsor()2754 .cloned()2755 } else {2756 None2757 }2758 }2759 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2760 let collection = <CollectionById<T>>::get(collection_id)?;2761 2762 let mut sponsor_transfer = false;2763 if collection.sponsorship.confirmed() {27642765 let collection_limits = collection.limits;2766 let collection_mode = collection.mode;2767 2768 2769 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2770 sponsor_transfer = match collection_mode {2771 CollectionMode::NFT => {2772 2773 2774 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2775 collection_limits.sponsor_transfer_timeout2776 } else {2777 ChainLimit::get().nft_sponsor_transfer_timeout2778 };2779 2780 let mut sponsored = true;2781 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2782 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2783 let limit_time = last_tx_block + limit.into();2784 if block_number <= limit_time {2785 sponsored = false;2786 }2787 }2788 if sponsored {2789 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2790 }27912792 sponsored2793 }2794 CollectionMode::Fungible(_) => {2795 2796 2797 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2798 collection_limits.sponsor_transfer_timeout2799 } else {2800 ChainLimit::get().fungible_sponsor_transfer_timeout2801 };2802 2803 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2804 let mut sponsored = true;2805 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2806 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2807 let limit_time = last_tx_block + limit.into();2808 if block_number <= limit_time {2809 sponsored = false;2810 }2811 }2812 if sponsored {2813 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2814 }28152816 sponsored2817 }2818 CollectionMode::ReFungible => {2819 2820 2821 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2822 collection_limits.sponsor_transfer_timeout2823 } else {2824 ChainLimit::get().refungible_sponsor_transfer_timeout2825 };2826 2827 let mut sponsored = true;2828 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2829 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2830 let limit_time = last_tx_block + limit.into();2831 if block_number <= limit_time {2832 sponsored = false;2833 }2834 }2835 if sponsored {2836 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2837 }28382839 sponsored2840 }2841 _ => {2842 false2843 },2844 };2845 }28462847 if !sponsor_transfer {2848 None2849 } else {2850 collection.sponsorship.sponsor()2851 .cloned()2852 }2853 }28542855 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2856 let mut sponsor_metadata_changes = false;28572858 let collection = <CollectionById<T>>::get(collection_id)?;28592860 if2861 collection.sponsorship.confirmed() &&2862 2863 2864 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2865 data.len() <= collection.limits.sponsored_data_size as usize2866 {2867 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2868 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28692870 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2871 .map(|last_block| block_number - last_block > rate_limit)2872 .unwrap_or(true) 2873 {2874 sponsor_metadata_changes = true;2875 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2876 }2877 }2878 }28792880 if !sponsor_metadata_changes {2881 None2882 } else {2883 collection.sponsorship.sponsor().cloned()2884 }2885 }28862887 _ => None,2888 })();28892890 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2891 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28922893 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28942895 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2896 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2897 2898 if !owned_contract && white_list_enabled {2899 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2900 return Err(InvalidTransaction::Call.into());2901 }2902 }2903 },2904 _ => {},2905 }29062907 2908 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29092910 2911 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29122913 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2914 &who,2915 code_hash,2916 salt,2917 );2918 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29192920 None2921 },29222923 2924 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29252926 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2927 &who,2928 &T::Hashing::hash(&_code),2929 _salt,2930 );29312932 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29332934 None2935 }29362937 2938 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29392940 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29412942 let mut sponsor_transfer = false;2943 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2944 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2945 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2946 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2947 let limit_time = last_tx_block + rate_limit;29482949 if block_number >= limit_time {2950 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2951 sponsor_transfer = true;2952 }2953 } else {2954 sponsor_transfer = false;2955 }2956 2957 if sponsor_transfer {2958 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2959 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2960 return Some(called_contract);2961 }2962 }2963 }29642965 None2966 },29672968 _ => None,2969 });29702971 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29722973 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2974 .map(|i| (fee, i))2975 }2976}297729782979impl<T: Config + Send + Sync> SignedExtension2980 for ChargeTransactionPayment<T>2981where2982 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2983 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2984 T::AccountId: AsRef<[u8]>,2985 T::AccountId: UncheckedFrom<T::Hash>,2986{2987 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2988 type AccountId = T::AccountId;2989 type Call = T::Call;2990 type AdditionalSigned = ();2991 type Pre = (2992 2993 BalanceOf<T>,2994 2995 Self::AccountId,2996 2997 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2998 );2999 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3000 Ok(())3001 }30023003 fn validate(3004 &self,3005 who: &Self::AccountId,3006 call: &Self::Call,3007 info: &DispatchInfoOf<Self::Call>,3008 len: usize,3009 ) -> TransactionValidity {3010 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3011 Ok(ValidTransaction {3012 priority: Self::get_priority(len, info, fee),3013 ..Default::default()3014 })3015 }30163017 fn pre_dispatch(3018 self,3019 who: &Self::AccountId,3020 call: &Self::Call,3021 info: &DispatchInfoOf<Self::Call>,3022 len: usize,3023 ) -> Result<Self::Pre, TransactionValidityError> {3024 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3025 Ok((self.0, who.clone(), imbalance))3026 }30273028 fn post_dispatch(3029 pre: Self::Pre,3030 info: &DispatchInfoOf<Self::Call>,3031 post_info: &PostDispatchInfoOf<Self::Call>,3032 len: usize,3033 _result: &DispatchResult,3034 ) -> Result<(), TransactionValidityError> {3035 let (tip, who, imbalance) = pre;3036 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3037 len as u32,3038 info,3039 post_info,3040 tip,3041 );3042 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3043 Ok(())3044 }3045}3046304730483049sp_api::decl_runtime_apis! {3050 pub trait NftApi {3051 3052 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3053 }3054}