123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]91011pub use serde::{Serialize, Deserialize};1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16 construct_runtime, decl_event, decl_module, decl_storage, decl_error,17 dispatch::DispatchResult,18 ensure, fail, parameter_types,19 traits::{20 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21 Randomness, IsSubType, WithdrawReasons,22 },23 weights::{24 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26 WeightToFeePolynomial, DispatchClass,27 },28 StorageValue,29 transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36 traits::{37 Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38 },39 transaction_validity::{40 TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41 },42 FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_ethereum::EthereumTransactionSender;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::NftErcSupport;59pub use eth::account::*;60use eth::erc::{ERC20Events, ERC721Events};6162pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;63pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;64pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;65pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6667686970pub type CollectionId = u32;71pub type TokenId = u32;72pub type DecimalPoints = u8;7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[derive(Serialize, Deserialize)]76pub enum CollectionMode {77 Invalid,78 NFT,79 80 Fungible(DecimalPoints),81 ReFungible,82}8384impl Default for CollectionMode {85 fn default() -> Self {86 Self::Invalid87 }88}8990impl Into<u8> for CollectionMode {91 fn into(self) -> u8 {92 match self {93 CollectionMode::Invalid => 0,94 CollectionMode::NFT => 1,95 CollectionMode::Fungible(_) => 2,96 CollectionMode::ReFungible => 3,97 }98 }99}100101#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AccessMode {104 Normal,105 WhiteList,106}107impl Default for AccessMode {108 fn default() -> Self {109 Self::Normal110 }111}112113#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]114#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]115pub enum SchemaVersion {116 ImageURL,117 Unique,118}119impl Default for SchemaVersion {120 fn default() -> Self {121 Self::ImageURL122 }123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct Ownership<AccountId> {128 pub owner: AccountId,129 pub fraction: u128,130}131132#[derive(Encode, Decode, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub enum SponsorshipState<AccountId> {135 136 Disabled,137 Unconfirmed(AccountId),138 139 Confirmed(AccountId),140}141142impl<AccountId> SponsorshipState<AccountId> {143 fn sponsor(&self) -> Option<&AccountId> {144 match self {145 Self::Confirmed(sponsor) => Some(sponsor),146 _ => None,147 }148 }149150 fn pending_sponsor(&self) -> Option<&AccountId> {151 match self {152 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),153 _ => None,154 }155 }156157 fn confirmed(&self) -> bool {158 matches!(self, Self::Confirmed(_))159 }160}161162impl<T> Default for SponsorshipState<T> {163 fn default() -> Self {164 Self::Disabled165 }166}167168#[derive(Encode, Decode, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct Collection<T: Config> {171 pub owner: T::AccountId,172 pub mode: CollectionMode,173 pub access: AccessMode,174 pub decimal_points: DecimalPoints,175 pub name: Vec<u16>, 176 pub description: Vec<u16>, 177 pub token_prefix: Vec<u8>, 178 pub mint_mode: bool,179 pub offchain_schema: Vec<u8>,180 pub schema_version: SchemaVersion,181 pub sponsorship: SponsorshipState<T::AccountId>,182 pub limits: CollectionLimits<T::BlockNumber>, 183 pub variable_on_chain_schema: Vec<u8>, 184 pub const_on_chain_schema: Vec<u8>, 185}186187pub struct CollectionHandle<T: Config> {188 pub id: CollectionId,189 collection: Collection<T>,190 logs: eth::log::LogRecorder,191}192impl<T: Config> CollectionHandle<T> {193 pub fn get(id: CollectionId) -> Option<Self> {194 <CollectionById<T>>::get(id)195 .map(|collection| Self {196 id,197 collection,198 logs: eth::log::LogRecorder::default(),199 })200 }201 pub fn log(&self, log: impl evm_coder::ToLog) {202 self.logs.log(log.to_log(self.evm_address))203 }204 pub fn into_inner(self) -> Collection<T> {205 self.collection.clone()206 }207}208209impl<T: Config> Deref for CollectionHandle<T> {210 type Target = Collection<T>;211212 fn deref(&self) -> &Self::Target {213 &self.collection214 }215}216217impl<T: Config> DerefMut for CollectionHandle<T> {218 fn deref_mut(&mut self) -> &mut Self::Target {219 &mut self.collection220 }221}222223#[derive(Encode, Decode, Debug, Clone, PartialEq)]224#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]225pub struct NftItemType<AccountId> {226 pub owner: AccountId,227 pub const_data: Vec<u8>,228 pub variable_data: Vec<u8>,229}230231#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]232#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]233pub struct FungibleItemType {234 pub value: u128,235}236237#[derive(Encode, Decode, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct ReFungibleItemType<AccountId> {240 pub owner: Vec<Ownership<AccountId>>,241 pub const_data: Vec<u8>,242 pub variable_data: Vec<u8>,243}244245246247248249250251252253254255256#[derive(Encode, Decode, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CollectionLimits<BlockNumber: Encode + Decode> {259 pub account_token_ownership_limit: u32,260 pub sponsored_data_size: u32,261 262 263 264 pub sponsored_data_rate_limit: Option<BlockNumber>,265 pub token_limit: u32,266267 268 pub sponsor_transfer_timeout: u32,269 pub owner_can_transfer: bool,270 pub owner_can_destroy: bool,271}272273impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {274 fn default() -> Self {275 Self { 276 account_token_ownership_limit: 10_000_000, 277 token_limit: u32::max_value(),278 sponsored_data_size: u32::MAX, 279 sponsored_data_rate_limit: None,280 sponsor_transfer_timeout: 14400,281 owner_can_transfer: true,282 owner_can_destroy: true283 }284 }285}286287#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]288#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]289pub struct ChainLimits {290 pub collection_numbers_limit: u32,291 pub account_token_ownership_limit: u32,292 pub collections_admins_limit: u64,293 pub custom_data_limit: u32,294295 296 pub nft_sponsor_transfer_timeout: u32,297 pub fungible_sponsor_transfer_timeout: u32,298 pub refungible_sponsor_transfer_timeout: u32,299300 301 pub offchain_schema_limit: u32,302 pub variable_on_chain_schema_limit: u32,303 pub const_on_chain_schema_limit: u32,304}305306pub trait WeightInfo {307 fn create_collection() -> Weight;308 fn destroy_collection() -> Weight;309 fn add_to_white_list() -> Weight;310 fn remove_from_white_list() -> Weight;311 fn set_public_access_mode() -> Weight;312 fn set_mint_permission() -> Weight;313 fn change_collection_owner() -> Weight;314 fn add_collection_admin() -> Weight;315 fn remove_collection_admin() -> Weight;316 fn set_collection_sponsor() -> Weight;317 fn confirm_sponsorship() -> Weight;318 fn remove_collection_sponsor() -> Weight;319 fn create_item(s: usize) -> Weight;320 fn burn_item() -> Weight;321 fn transfer() -> Weight;322 fn approve() -> Weight;323 fn transfer_from() -> Weight;324 fn set_offchain_schema() -> Weight;325 fn set_const_on_chain_schema() -> Weight;326 fn set_variable_on_chain_schema() -> Weight;327 fn set_variable_meta_data() -> Weight;328 fn enable_contract_sponsoring() -> Weight;329 fn set_schema_version() -> Weight;330 fn set_chain_limits() -> Weight;331 fn set_contract_sponsoring_rate_limit() -> Weight;332 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;333 fn toggle_contract_white_list() -> Weight;334 fn add_to_contract_white_list() -> Weight;335 fn remove_from_contract_white_list() -> Weight;336 fn set_collection_limits() -> Weight;337}338339#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]340#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]341pub struct CreateNftData {342 pub const_data: Vec<u8>,343 pub variable_data: Vec<u8>,344}345346#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]347#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]348pub struct CreateFungibleData {349 pub value: u128,350}351352#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]353#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]354pub struct CreateReFungibleData {355 pub const_data: Vec<u8>,356 pub variable_data: Vec<u8>,357 pub pieces: u128,358}359360#[derive(Encode, Decode, Debug, Clone, PartialEq)]361#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]362pub enum CreateItemData {363 NFT(CreateNftData),364 Fungible(CreateFungibleData),365 ReFungible(CreateReFungibleData),366}367368impl CreateItemData {369 pub fn len(&self) -> usize {370 let len = match self {371 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),372 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),373 _ => 0374 };375 376 return len;377 }378}379380impl From<CreateNftData> for CreateItemData {381 fn from(item: CreateNftData) -> Self {382 CreateItemData::NFT(item)383 }384}385386impl From<CreateReFungibleData> for CreateItemData {387 fn from(item: CreateReFungibleData) -> Self {388 CreateItemData::ReFungible(item)389 }390}391392impl From<CreateFungibleData> for CreateItemData {393 fn from(item: CreateFungibleData) -> Self {394 CreateItemData::Fungible(item)395 }396}397398399decl_error! {400 401 pub enum Error for Module<T: Config> {402 403 TotalCollectionsLimitExceeded,404 405 CollectionDecimalPointLimitExceeded, 406 407 CollectionNameLimitExceeded, 408 409 CollectionDescriptionLimitExceeded, 410 411 CollectionTokenPrefixLimitExceeded,412 413 CollectionNotFound,414 415 TokenNotFound,416 417 AdminNotFound,418 419 NumOverflow, 420 421 AlreadyAdmin, 422 423 NoPermission,424 425 ConfirmUnsetSponsorFail,426 427 PublicMintingNotAllowed,428 429 MustBeTokenOwner,430 431 TokenValueTooLow,432 433 NftSizeLimitExceeded,434 435 ApproveNotFound,436 437 TokenValueNotEnough,438 439 ApproveRequired,440 441 AddresNotInWhiteList,442 443 CollectionAdminsLimitExceeded,444 445 AddressOwnershipLimitExceeded,446 447 EmptyArgument,448 449 TokenConstDataLimitExceeded,450 451 TokenVariableDataLimitExceeded,452 453 NotNftDataUsedToMintNftCollectionToken,454 455 NotFungibleDataUsedToMintFungibleCollectionToken,456 457 NotReFungibleDataUsedToMintReFungibleCollectionToken,458 459 UnexpectedCollectionType,460 461 CantStoreMetadataInFungibleTokens,462 463 CollectionTokenLimitExceeded,464 465 AccountTokenLimitExceeded,466 467 CollectionLimitBoundsExceeded,468 469 OwnerPermissionsCantBeReverted,470 471 SchemaDataLimitExceeded,472 473 WrongRefungiblePieces,474 475 BadCreateRefungibleCall,476 }477}478479pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {480 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;481482 483 type WeightInfo: WeightInfo;484485 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;486 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;487 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;488489 type CrossAccountId: CrossAccountId<Self::AccountId>;490 type Currency: Currency<Self::AccountId>;491 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;492 type TreasuryAccountId: Get<Self::AccountId>;493494 type EthereumChainId: Get<u64>;495 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;496}497498#[cfg(feature = "runtime-benchmarks")]499mod benchmarking;500501502503504505506507508509510511512513514515516517518519520521522523524525decl_storage! {526 trait Store for Module<T: Config> as Nft {527528 529 530 CreatedCollectionCount: u32;531 532 ChainVersion: u64;533 534 535 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;536 537538 539 pub ChainLimit get(fn chain_limit) config(): ChainLimits;540 541542 543 544 545 DestroyedCollectionCount: u32;546 547 548 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;549 550551 552 553 554 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;555 556 557 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;558 559 560 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;561 562563 564 565 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;566567 568 569 570 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;571572 573 574 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;575 576 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;577 578 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;579 580581 582 583 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;584 585586 587 588 589 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;590 591 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;592 593 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;594 595 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;596 597598 599 600 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;601 602 603 604 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;605 606 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;607 608 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;609 610 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;611 612 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 613 614 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 615 616 }617 add_extra_genesis {618 build(|config: &GenesisConfig<T>| {619 620 for (_num, _c) in &config.collection_id {621 <Module<T>>::init_collection(_c);622 }623624 for (_num, _c, _i) in &config.nft_item_id {625 <Module<T>>::init_nft_token(*_c, _i);626 }627628 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {629 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);630 }631632 for (_num, _c, _i) in &config.refungible_item_id {633 <Module<T>>::init_refungible_token(*_c, _i);634 }635 })636 }637}638639decl_event!(640 pub enum Event<T>641 where642 AccountId = <T as frame_system::Config>::AccountId,643 CrossAccountId = <T as Config>::CrossAccountId,644 {645 646 647 648 649 650 651 652 653 654 CollectionCreated(CollectionId, u8, AccountId),655656 657 658 659 660 661 662 663 664 665 ItemCreated(CollectionId, TokenId, CrossAccountId),666667 668 669 670 671 672 673 674 ItemDestroyed(CollectionId, TokenId),675676 677 678 679 680 681 682 683 684 685 686 687 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),688689 690 691 692 693 694 695 696 697 698 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),699 }700);701702decl_module! {703 pub struct Module<T: Config> for enum Call 704 where 705 origin: T::Origin706 {707 fn deposit_event() = default;708 type Error = Error<T>;709710 fn on_initialize(now: T::BlockNumber) -> Weight {711 0712 }713714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 #[weight = <T as Config>::WeightInfo::create_collection()]731 #[transactional]732 pub fn create_collection(origin,733 collection_name: Vec<u16>,734 collection_description: Vec<u16>,735 token_prefix: Vec<u8>,736 mode: CollectionMode) -> DispatchResult {737738 739 let who = ensure_signed(origin)?;740741 742 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();743 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(744 &T::TreasuryAccountId::get(),745 T::CollectionCreationPrice::get(),746 ));747 <T as Config>::Currency::settle(748 &who,749 imbalance,750 WithdrawReasons::TRANSFER,751 ExistenceRequirement::KeepAlive,752 ).map_err(|_| Error::<T>::NoPermission)?;753754 let decimal_points = match mode {755 CollectionMode::Fungible(points) => points,756 _ => 0757 };758759 let chain_limit = ChainLimit::get();760761 let created_count = CreatedCollectionCount::get();762 let destroyed_count = DestroyedCollectionCount::get();763764 765 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);766767 768 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);769 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);770 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);771 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);772773 774 let next_id = created_count775 .checked_add(1)776 .ok_or(Error::<T>::NumOverflow)?;777778 CreatedCollectionCount::put(next_id);779780 let limits = CollectionLimits {781 sponsored_data_size: chain_limit.custom_data_limit,782 ..Default::default()783 };784785 786 let new_collection = Collection {787 owner: who.clone(),788 name: collection_name,789 mode: mode.clone(),790 mint_mode: false,791 access: AccessMode::Normal,792 description: collection_description,793 decimal_points: decimal_points,794 token_prefix: token_prefix,795 offchain_schema: Vec::new(),796 schema_version: SchemaVersion::ImageURL,797 sponsorship: SponsorshipState::Disabled,798 variable_on_chain_schema: Vec::new(),799 const_on_chain_schema: Vec::new(),800 limits,801 };802803 804 <CollectionById<T>>::insert(next_id, new_collection);805806 807 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));808809 Ok(())810 }811812 813 814 815 816 817 818 819 820 821 #[weight = <T as Config>::WeightInfo::destroy_collection()]822 #[transactional]823 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {824825 let sender = ensure_signed(origin)?;826 let collection = Self::get_collection(collection_id)?;827 Self::check_owner_permissions(&collection, &sender)?;828 if !collection.limits.owner_can_destroy {829 fail!(Error::<T>::NoPermission);830 }831832 <AddressTokens<T>>::remove_prefix(collection_id);833 <Allowances<T>>::remove_prefix(collection_id);834 <Balance<T>>::remove_prefix(collection_id);835 <ItemListIndex>::remove(collection_id);836 <AdminList<T>>::remove(collection_id);837 <CollectionById<T>>::remove(collection_id);838 <WhiteList<T>>::remove_prefix(collection_id);839840 <NftItemList<T>>::remove_prefix(collection_id);841 <FungibleItemList<T>>::remove_prefix(collection_id);842 <ReFungibleItemList<T>>::remove_prefix(collection_id);843844 <NftTransferBasket<T>>::remove_prefix(collection_id);845 <FungibleTransferBasket<T>>::remove_prefix(collection_id);846 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);847848 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);849850 DestroyedCollectionCount::put(DestroyedCollectionCount::get()851 .checked_add(1)852 .ok_or(Error::<T>::NumOverflow)?);853854 Ok(())855 }856857 858 859 860 861 862 863 864 865 866 867 868 869 #[weight = <T as Config>::WeightInfo::add_to_white_list()]870 #[transactional]871 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{872873 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);874 let collection = Self::get_collection(collection_id)?;875876 Self::toggle_white_list_internal(877 &sender,878 &collection,879 &address,880 true,881 )?;882883 Ok(())884 }885886 887 888 889 890 891 892 893 894 895 896 897 898 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]899 #[transactional]900 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{901902 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);903 let collection = Self::get_collection(collection_id)?;904905 Self::toggle_white_list_internal(906 &sender,907 &collection,908 &address,909 false,910 )?;911912 Ok(())913 }914915 916 917 918 919 920 921 922 923 924 925 926 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]927 #[transactional]928 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult929 {930 let sender = ensure_signed(origin)?;931932 let mut target_collection = Self::get_collection(collection_id)?;933 Self::check_owner_permissions(&target_collection, &sender)?;934 target_collection.access = mode;935 Self::save_collection(target_collection);936937 Ok(())938 }939940 941 942 943 944 945 946 947 948 949 950 951 952 953 #[weight = <T as Config>::WeightInfo::set_mint_permission()]954 #[transactional]955 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult956 {957 let sender = ensure_signed(origin)?;958959 let mut target_collection = Self::get_collection(collection_id)?;960 Self::check_owner_permissions(&target_collection, &sender)?;961 target_collection.mint_mode = mint_permission;962 Self::save_collection(target_collection);963964 Ok(())965 }966967 968 969 970 971 972 973 974 975 976 977 978 #[weight = <T as Config>::WeightInfo::change_collection_owner()]979 #[transactional]980 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {981982 let sender = ensure_signed(origin)?;983 let mut target_collection = Self::get_collection(collection_id)?;984 Self::check_owner_permissions(&target_collection, &sender)?;985 target_collection.owner = new_owner;986 Self::save_collection(target_collection);987988 Ok(())989 }990991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 #[weight = <T as Config>::WeightInfo::add_collection_admin()]1005 #[transactional]1006 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1007 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1008 let collection = Self::get_collection(collection_id)?;1009 Self::check_owner_or_admin_permissions(&collection, &sender)?;1010 let mut admin_arr = <AdminList<T>>::get(collection_id);10111012 match admin_arr.binary_search(&new_admin_id) {1013 Ok(_) => {},1014 Err(idx) => {1015 let limits = ChainLimit::get();1016 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1017 admin_arr.insert(idx, new_admin_id);1018 <AdminList<T>>::insert(collection_id, admin_arr);1019 }1020 }1021 Ok(())1022 }10231024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1037 #[transactional]1038 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1039 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1040 let collection = Self::get_collection(collection_id)?;1041 Self::check_owner_or_admin_permissions(&collection, &sender)?;1042 let mut admin_arr = <AdminList<T>>::get(collection_id);10431044 match admin_arr.binary_search(&account_id) {1045 Ok(idx) => {1046 admin_arr.remove(idx);1047 <AdminList<T>>::insert(collection_id, admin_arr);1048 },1049 Err(_) => {}1050 }1051 Ok(())1052 }10531054 1055 1056 1057 1058 1059 1060 1061 1062 1063 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1064 #[transactional]1065 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1066 let sender = ensure_signed(origin)?;1067 let mut target_collection = Self::get_collection(collection_id)?;1068 Self::check_owner_permissions(&target_collection, &sender)?;10691070 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1071 Self::save_collection(target_collection);10721073 Ok(())1074 }10751076 1077 1078 1079 1080 1081 1082 1083 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1084 #[transactional]1085 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1086 let sender = ensure_signed(origin)?;10871088 let mut target_collection = Self::get_collection(collection_id)?;1089 ensure!(1090 target_collection.sponsorship.pending_sponsor() == Some(&sender),1091 Error::<T>::ConfirmUnsetSponsorFail1092 );10931094 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1095 Self::save_collection(target_collection);10961097 Ok(())1098 }10991100 1101 1102 1103 1104 1105 1106 1107 1108 1109 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1110 #[transactional]1111 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1112 let sender = ensure_signed(origin)?;11131114 let mut target_collection = Self::get_collection(collection_id)?;1115 Self::check_owner_permissions(&target_collection, &sender)?;11161117 target_collection.sponsorship = SponsorshipState::Disabled;1118 Self::save_collection(target_collection);11191120 Ok(())1121 }11221123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 11461147 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1148 #[transactional]1149 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1150 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1151 let collection = Self::get_collection(collection_id)?;11521153 Self::create_item_internal(&sender, &collection, &owner, data)?;11541155 Self::submit_logs(collection)?;1156 Ok(())1157 }11581159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1178 .map(|data| { data.len() })1179 .sum())]1180 #[transactional]1181 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11821183 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1184 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1185 let collection = Self::get_collection(collection_id)?;11861187 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11881189 Self::submit_logs(collection)?;1190 Ok(())1191 }11921193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 #[weight = <T as Config>::WeightInfo::burn_item()]1207 #[transactional]1208 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12091210 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1211 let target_collection = Self::get_collection(collection_id)?;12121213 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12141215 Self::submit_logs(target_collection)?;1216 Ok(())1217 }12181219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 #[weight = <T as Config>::WeightInfo::transfer()]1243 #[transactional]1244 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1245 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1246 let collection = Self::get_collection(collection_id)?;12471248 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12491250 Self::submit_logs(collection)?;1251 Ok(())1252 }12531254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 #[weight = <T as Config>::WeightInfo::approve()]1270 #[transactional]1271 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1272 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1273 let collection = Self::get_collection(collection_id)?;12741275 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12761277 Self::submit_logs(collection)?;1278 Ok(())1279 }1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 #[weight = <T as Config>::WeightInfo::transfer_from()]1301 #[transactional]1302 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1303 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1304 let collection = Self::get_collection(collection_id)?;13051306 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13071308 Self::submit_logs(collection)?;1309 Ok(())1310 }1311 1312 1313 1314 1315 13161317 13181319 13201321 1322 13231324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1337 #[transactional]1338 pub fn set_variable_meta_data (1339 origin,1340 collection_id: CollectionId,1341 item_id: TokenId,1342 data: Vec<u8>1343 ) -> DispatchResult {1344 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1345 1346 let collection = Self::get_collection(collection_id)?;13471348 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13491350 Ok(())1351 }1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 #[weight = <T as Config>::WeightInfo::set_schema_version()]1368 #[transactional]1369 pub fn set_schema_version(1370 origin,1371 collection_id: CollectionId,1372 version: SchemaVersion1373 ) -> DispatchResult {1374 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1375 let mut target_collection = Self::get_collection(collection_id)?;1376 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1377 target_collection.schema_version = version;1378 Self::save_collection(target_collection);13791380 Ok(())1381 }13821383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1396 #[transactional]1397 pub fn set_offchain_schema(1398 origin,1399 collection_id: CollectionId,1400 schema: Vec<u8>1401 ) -> DispatchResult {1402 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1403 let mut target_collection = Self::get_collection(collection_id)?;1404 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14051406 1407 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14081409 target_collection.offchain_schema = schema;1410 Self::save_collection(target_collection);14111412 Ok(())1413 }14141415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1428 #[transactional]1429 pub fn set_const_on_chain_schema (1430 origin,1431 collection_id: CollectionId,1432 schema: Vec<u8>1433 ) -> DispatchResult {1434 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1435 let mut target_collection = Self::get_collection(collection_id)?;1436 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14371438 1439 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14401441 target_collection.const_on_chain_schema = schema;1442 Self::save_collection(target_collection);14431444 Ok(())1445 }14461447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1460 #[transactional]1461 pub fn set_variable_on_chain_schema (1462 origin,1463 collection_id: CollectionId,1464 schema: Vec<u8>1465 ) -> DispatchResult {1466 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1467 let mut target_collection = Self::get_collection(collection_id)?;1468 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14691470 1471 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14721473 target_collection.variable_on_chain_schema = schema;1474 Self::save_collection(target_collection);14751476 Ok(())1477 }14781479 1480 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1481 #[transactional]1482 pub fn set_chain_limits(1483 origin,1484 limits: ChainLimits1485 ) -> DispatchResult {14861487 #[cfg(not(feature = "runtime-benchmarks"))]1488 ensure_root(origin)?;14891490 <ChainLimit>::put(limits);1491 Ok(())1492 }14931494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1506 #[transactional]1507 pub fn enable_contract_sponsoring(1508 origin,1509 contract_address: T::AccountId,1510 enable: bool1511 ) -> DispatchResult {15121513 let sender = ensure_signed(origin)?;15141515 #[cfg(feature = "runtime-benchmarks")]1516 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15171518 Self::ensure_contract_owned(sender, &contract_address)?;15191520 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1521 Ok(())1522 }15231524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1542 #[transactional]1543 pub fn set_contract_sponsoring_rate_limit(1544 origin,1545 contract_address: T::AccountId,1546 rate_limit: T::BlockNumber1547 ) -> DispatchResult {1548 let sender = ensure_signed(origin)?;15491550 #[cfg(feature = "runtime-benchmarks")]1551 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15521553 Self::ensure_contract_owned(sender, &contract_address)?;1554 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1555 Ok(())1556 }15571558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1570 #[transactional]1571 pub fn toggle_contract_white_list(1572 origin,1573 contract_address: T::AccountId,1574 enable: bool1575 ) -> DispatchResult {1576 let sender = ensure_signed(origin)?;15771578 #[cfg(feature = "runtime-benchmarks")]1579 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15801581 Self::ensure_contract_owned(sender, &contract_address)?;1582 if enable {1583 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1584 } else {1585 <ContractWhiteListEnabled<T>>::remove(contract_address);1586 }1587 Ok(())1588 }1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1602 #[transactional]1603 pub fn add_to_contract_white_list(1604 origin,1605 contract_address: T::AccountId,1606 account_address: T::AccountId1607 ) -> DispatchResult {1608 let sender = ensure_signed(origin)?;16091610 #[cfg(feature = "runtime-benchmarks")]1611 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1612 1613 Self::ensure_contract_owned(sender, &contract_address)?; 1614 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1615 Ok(())1616 }16171618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1630 #[transactional]1631 pub fn remove_from_contract_white_list(1632 origin,1633 contract_address: T::AccountId,1634 account_address: T::AccountId1635 ) -> DispatchResult {1636 let sender = ensure_signed(origin)?;16371638 #[cfg(feature = "runtime-benchmarks")]1639 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16401641 Self::ensure_contract_owned(sender, &contract_address)?;1642 <ContractWhiteList<T>>::remove(contract_address, account_address);1643 Ok(())1644 }16451646 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1647 #[transactional]1648 pub fn set_collection_limits(1649 origin,1650 collection_id: u32,1651 new_limits: CollectionLimits<T::BlockNumber>,1652 ) -> DispatchResult {1653 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1654 let mut target_collection = Self::get_collection(collection_id)?;1655 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1656 let old_limits = &target_collection.limits;1657 let chain_limits = ChainLimit::get();16581659 1660 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1661 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1662 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1663 Error::<T>::CollectionLimitBoundsExceeded);16641665 1666 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1667 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16681669 ensure!(1670 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1671 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1672 Error::<T>::OwnerPermissionsCantBeReverted,1673 );16741675 target_collection.limits = new_limits;1676 Self::save_collection(target_collection);16771678 Ok(())1679 } 1680 }1681}16821683impl<T: Config> Module<T> {1684 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1685 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1686 Self::validate_create_item_args(&collection, &data)?;1687 Self::create_item_no_validation(&collection, owner, data)?;16881689 Ok(())1690 }16911692 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1693 1694 Self::is_correct_transfer(target_collection, &recipient)?;16951696 1697 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1698 Self::is_owner_or_admin_permissions(target_collection, &sender),1699 Error::<T>::NoPermission);17001701 if target_collection.access == AccessMode::WhiteList {1702 Self::check_white_list(target_collection, &sender)?;1703 Self::check_white_list(target_collection, &recipient)?;1704 }17051706 match target_collection.mode1707 {1708 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1709 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1710 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1711 _ => ()1712 };17131714 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17151716 Ok(())1717 }17181719 pub fn approve_internal(1720 sender: &T::CrossAccountId,1721 spender: &T::CrossAccountId,1722 collection: &CollectionHandle<T>,1723 item_id: TokenId,1724 amount: u1281725 ) -> DispatchResult {1726 Self::token_exists(&collection, item_id)?;17271728 1729 let bypasses_limits = collection.limits.owner_can_transfer &&1730 Self::is_owner_or_admin_permissions(1731 &collection,1732 &sender,1733 );17341735 let allowance_limit = if bypasses_limits {1736 None1737 } else if let Some(amount) = Self::owned_amount(1738 &sender,1739 &collection,1740 item_id,1741 ) {1742 Some(amount)1743 } else {1744 fail!(Error::<T>::NoPermission);1745 };17461747 if collection.access == AccessMode::WhiteList {1748 Self::check_white_list(&collection, &sender)?;1749 Self::check_white_list(&collection, &spender)?;1750 }17511752 let allowance: u128 = amount1753 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1754 .ok_or(Error::<T>::NumOverflow)?;1755 if let Some(limit) = allowance_limit {1756 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1757 }1758 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17591760 if matches!(collection.mode, CollectionMode::NFT) {1761 1762 collection.log(ERC721Events::Approval {1763 owner: *sender.as_eth(),1764 approved: *spender.as_eth(),1765 token_id: item_id.into(),1766 });1767 }17681769 if matches!(collection.mode, CollectionMode::Fungible(_)) {1770 1771 collection.log(ERC20Events::Approval {1772 owner: *sender.as_eth(),1773 spender: *spender.as_eth(),1774 value: allowance.into()1775 });1776 }17771778 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1779 Ok(())1780 }17811782 pub fn transfer_from_internal(1783 sender: &T::CrossAccountId,1784 from: &T::CrossAccountId,1785 recipient: &T::CrossAccountId,1786 collection: &CollectionHandle<T>,1787 item_id: TokenId,1788 amount: u128,1789 ) -> DispatchResult {1790 1791 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17921793 1794 Self::is_correct_transfer(&collection, &recipient)?;17951796 1797 ensure!(1798 approval >= amount || 1799 (1800 collection.limits.owner_can_transfer &&1801 Self::is_owner_or_admin_permissions(&collection, &sender)1802 ),1803 Error::<T>::NoPermission1804 );18051806 if collection.access == AccessMode::WhiteList {1807 Self::check_white_list(&collection, &sender)?;1808 Self::check_white_list(&collection, &recipient)?;1809 }18101811 1812 let allowance = approval.saturating_sub(amount);1813 if allowance > 0 {1814 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1815 } else {1816 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1817 }18181819 match collection.mode {1820 CollectionMode::NFT => {1821 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1822 }1823 CollectionMode::Fungible(_) => {1824 Self::transfer_fungible(&collection, amount, &from, &recipient)?1825 }1826 CollectionMode::ReFungible => {1827 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1828 }1829 _ => ()1830 };18311832 if matches!(collection.mode, CollectionMode::Fungible(_)) {1833 collection.log(ERC20Events::Approval {1834 owner: *from.as_eth(),1835 spender: *sender.as_eth(),1836 value: allowance.into()1837 });1838 }18391840 Ok(())1841 }18421843 pub fn set_variable_meta_data_internal(1844 sender: &T::CrossAccountId,1845 collection: &CollectionHandle<T>, 1846 item_id: TokenId,1847 data: Vec<u8>,1848 ) -> DispatchResult {1849 Self::token_exists(&collection, item_id)?;18501851 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18521853 1854 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1855 Self::is_owner_or_admin_permissions(&collection, &sender),1856 Error::<T>::NoPermission);18571858 match collection.mode1859 {1860 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1861 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1862 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1863 _ => fail!(Error::<T>::UnexpectedCollectionType)1864 };18651866 Ok(())1867 }18681869 pub fn create_multiple_items_internal(1870 sender: &T::CrossAccountId,1871 collection: &CollectionHandle<T>,1872 owner: &T::CrossAccountId,1873 items_data: Vec<CreateItemData>,1874 ) -> DispatchResult {1875 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18761877 for data in &items_data {1878 Self::validate_create_item_args(&collection, data)?;1879 }1880 for data in &items_data {1881 Self::create_item_no_validation(&collection, owner, data.clone())?;1882 }18831884 Ok(())1885 }18861887 pub fn burn_item_internal(1888 sender: &T::CrossAccountId,1889 collection: &CollectionHandle<T>,1890 item_id: TokenId,1891 value: u128,1892 ) -> DispatchResult {1893 ensure!(1894 Self::is_item_owner(&sender, &collection, item_id) ||1895 (1896 collection.limits.owner_can_transfer &&1897 Self::is_owner_or_admin_permissions(&collection, &sender)1898 ),1899 Error::<T>::NoPermission1900 );19011902 if collection.access == AccessMode::WhiteList {1903 Self::check_white_list(&collection, &sender)?;1904 }19051906 match collection.mode1907 {1908 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1909 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1910 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1911 _ => ()1912 };19131914 Ok(())1915 }19161917 pub fn toggle_white_list_internal(1918 sender: &T::CrossAccountId,1919 collection: &CollectionHandle<T>,1920 address: &T::CrossAccountId,1921 whitelisted: bool,1922 ) -> DispatchResult {1923 Self::check_owner_or_admin_permissions(&collection, &sender)?;19241925 if whitelisted {1926 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1927 } else {1928 <WhiteList<T>>::remove(collection.id, address.as_sub());1929 }19301931 Ok(())1932 }19331934 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1935 let collection_id = collection.id;19361937 1938 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1939 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1940 1941 Ok(())1942 }19431944 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1945 let collection_id = collection.id;19461947 1948 let total_items: u32 = ItemListIndex::get(collection_id)1949 .checked_add(amount)1950 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1951 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1952 .checked_add(amount)1953 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1954 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1955 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19561957 if !Self::is_owner_or_admin_permissions(collection, &sender) {1958 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1959 Self::check_white_list(collection, owner)?;1960 Self::check_white_list(collection, sender)?;1961 }19621963 Ok(())1964 }19651966 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1967 match target_collection.mode1968 {1969 CollectionMode::NFT => {1970 if let CreateItemData::NFT(data) = data {1971 1972 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1973 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1974 } else {1975 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1976 }1977 },1978 CollectionMode::Fungible(_) => {1979 if let CreateItemData::Fungible(_) = data {1980 } else {1981 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1982 }1983 },1984 CollectionMode::ReFungible => {1985 if let CreateItemData::ReFungible(data) = data {19861987 1988 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1989 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19901991 1992 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1993 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1994 } else {1995 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1996 }1997 },1998 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1999 };20002001 Ok(())2002 }20032004 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2005 match data2006 {2007 CreateItemData::NFT(data) => {2008 let item = NftItemType {2009 owner: owner.clone(),2010 const_data: data.const_data,2011 variable_data: data.variable_data2012 };20132014 Self::add_nft_item(collection, item)?;2015 },2016 CreateItemData::Fungible(data) => {2017 Self::add_fungible_item(collection, &owner, data.value)?;2018 },2019 CreateItemData::ReFungible(data) => {2020 let mut owner_list = Vec::new();2021 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20222023 let item = ReFungibleItemType {2024 owner: owner_list,2025 const_data: data.const_data,2026 variable_data: data.variable_data2027 };20282029 Self::add_refungible_item(collection, item)?;2030 }2031 };20322033 Ok(())2034 }20352036 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2037 let collection_id = collection.id;20382039 2040 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20412042 2043 let item = FungibleItemType {2044 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2045 };2046 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20472048 2049 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2050 .checked_add(value)2051 .ok_or(Error::<T>::NumOverflow)?;2052 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20532054 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2055 Ok(())2056 }20572058 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2059 let collection_id = collection.id;20602061 let current_index = <ItemListIndex>::get(collection_id)2062 .checked_add(1)2063 .ok_or(Error::<T>::NumOverflow)?;2064 let itemcopy = item.clone();20652066 ensure!(2067 item.owner.len() == 1,2068 Error::<T>::BadCreateRefungibleCall,2069 );2070 let item_owner = item.owner.first().expect("only one owner is defined");20712072 let value = item_owner.fraction;2073 let owner = item_owner.owner.clone();20742075 Self::add_token_index(collection_id, current_index, &owner)?;20762077 <ItemListIndex>::insert(collection_id, current_index);2078 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20792080 2081 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2082 .checked_add(value)2083 .ok_or(Error::<T>::NumOverflow)?;2084 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20852086 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2087 Ok(())2088 }20892090 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2091 let collection_id = collection.id;20922093 let current_index = <ItemListIndex>::get(collection_id)2094 .checked_add(1)2095 .ok_or(Error::<T>::NumOverflow)?;20962097 let item_owner = item.owner.clone();2098 Self::add_token_index(collection_id, current_index, &item.owner)?;20992100 <ItemListIndex>::insert(collection_id, current_index);2101 <NftItemList<T>>::insert(collection_id, current_index, item);21022103 2104 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2105 .checked_add(1)2106 .ok_or(Error::<T>::NumOverflow)?;2107 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21082109 collection.log(ERC721Events::Transfer {2110 from: H160::default(),2111 to: *item_owner.as_eth(),2112 token_id: current_index.into(),2113 });2114 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2115 Ok(())2116 }21172118 fn burn_refungible_item(2119 collection: &CollectionHandle<T>,2120 item_id: TokenId,2121 owner: &T::CrossAccountId,2122 ) -> DispatchResult {2123 let collection_id = collection.id;21242125 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2126 .ok_or(Error::<T>::TokenNotFound)?;2127 let rft_balance = token2128 .owner2129 .iter()2130 .find(|&i| i.owner == *owner)2131 .ok_or(Error::<T>::TokenNotFound)?;2132 Self::remove_token_index(collection_id, item_id, owner)?;21332134 2135 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2136 .checked_sub(rft_balance.fraction)2137 .ok_or(Error::<T>::NumOverflow)?;2138 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21392140 2141 let index = token2142 .owner2143 .iter()2144 .position(|i| i.owner == *owner)2145 .expect("owned item is exists");2146 token.owner.remove(index);2147 let owner_count = token.owner.len();21482149 2150 if owner_count == 0 {2151 <ReFungibleItemList<T>>::remove(collection_id, item_id);2152 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2153 }2154 else {2155 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2156 }21572158 Ok(())2159 }21602161 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2162 let collection_id = collection.id;21632164 let item = <NftItemList<T>>::get(collection_id, item_id)2165 .ok_or(Error::<T>::TokenNotFound)?;2166 Self::remove_token_index(collection_id, item_id, &item.owner)?;21672168 2169 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2170 .checked_sub(1)2171 .ok_or(Error::<T>::NumOverflow)?;2172 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2173 <NftItemList<T>>::remove(collection_id, item_id);2174 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21752176 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2177 Ok(())2178 }21792180 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2181 let collection_id = collection.id;21822183 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2184 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21852186 2187 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2188 .checked_sub(value)2189 .ok_or(Error::<T>::NumOverflow)?;2190 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21912192 if balance.value - value > 0 {2193 balance.value -= value;2194 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2195 }2196 else {2197 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2198 }21992200 collection.log(ERC20Events::Transfer {2201 from: *owner.as_eth(),2202 to: H160::default(),2203 value: value.into(),2204 });2205 Ok(())2206 }22072208 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2209 Ok(<CollectionHandle<T>>::get(collection_id)2210 .ok_or(Error::<T>::CollectionNotFound)?)2211 }22122213 fn save_collection(collection: CollectionHandle<T>) {2214 <CollectionById<T>>::insert(collection.id, collection.into_inner());2215 }22162217 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2218 if collection.logs.is_empty() {2219 return Ok(())2220 }2221 T::EthereumTransactionSender::submit_logs_transaction(2222 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2223 collection.logs.retrieve_logs(),2224 )2225 }22262227 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2228 ensure!(2229 *subject == target_collection.owner,2230 Error::<T>::NoPermission2231 );22322233 Ok(())2234 }22352236 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2237 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2238 }22392240 fn check_owner_or_admin_permissions(2241 collection: &CollectionHandle<T>,2242 subject: &T::CrossAccountId,2243 ) -> DispatchResult {2244 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22452246 Ok(())2247 }22482249 fn owned_amount(2250 subject: &T::CrossAccountId,2251 target_collection: &CollectionHandle<T>,2252 item_id: TokenId,2253 ) -> Option<u128> {2254 let collection_id = target_collection.id;22552256 match target_collection.mode {2257 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2258 .then(|| 1),2259 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2260 .value),2261 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2262 .owner2263 .iter()2264 .find(|i| i.owner == *subject)2265 .map(|i| i.fraction),2266 CollectionMode::Invalid => None,2267 }2268 }22692270 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2271 match target_collection.mode {2272 CollectionMode::Fungible(_) => true,2273 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2274 }2275 }22762277 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2278 let collection_id = collection.id;22792280 let mes = Error::<T>::AddresNotInWhiteList;2281 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22822283 Ok(())2284 }22852286 2287 2288 fn token_exists(2289 target_collection: &CollectionHandle<T>,2290 item_id: TokenId,2291 ) -> DispatchResult {2292 let collection_id = target_collection.id;2293 let exists = match target_collection.mode2294 {2295 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2296 CollectionMode::Fungible(_) => true,2297 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2298 _ => false2299 };23002301 ensure!(exists == true, Error::<T>::TokenNotFound);2302 Ok(())2303 }23042305 fn transfer_fungible(2306 collection: &CollectionHandle<T>,2307 value: u128,2308 owner: &T::CrossAccountId,2309 recipient: &T::CrossAccountId,2310 ) -> DispatchResult {2311 let collection_id = collection.id;23122313 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2314 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23152316 2317 Self::add_fungible_item(collection, recipient, value)?;23182319 2320 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23212322 2323 if balance.value == value {2324 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2325 }2326 else {2327 balance.value -= value;2328 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2329 }23302331 collection.log(ERC20Events::Transfer {2332 from: *owner.as_eth(),2333 to: *recipient.as_eth(),2334 value: value.into(),2335 });2336 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23372338 Ok(())2339 }23402341 fn transfer_refungible(2342 collection: &CollectionHandle<T>,2343 item_id: TokenId,2344 value: u128,2345 owner: T::CrossAccountId,2346 new_owner: T::CrossAccountId,2347 ) -> DispatchResult {2348 let collection_id = collection.id;2349 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2350 .ok_or(Error::<T>::TokenNotFound)?;23512352 let item = full_item2353 .owner2354 .iter()2355 .filter(|i| i.owner == owner)2356 .next()2357 .ok_or(Error::<T>::TokenNotFound)?;2358 let amount = item.fraction;23592360 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23612362 2363 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2364 .checked_sub(value)2365 .ok_or(Error::<T>::NumOverflow)?;2366 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23672368 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2369 .checked_add(value)2370 .ok_or(Error::<T>::NumOverflow)?;2371 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23722373 let old_owner = item.owner.clone();2374 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23752376 2377 if amount == value && !new_owner_has_account {2378 2379 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 .owner = new_owner.clone();2387 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23882389 2390 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2391 } else {2392 let mut new_full_item = full_item.clone();2393 new_full_item2394 .owner2395 .iter_mut()2396 .find(|i| i.owner == owner)2397 .expect("old owner does present in refungible")2398 .fraction -= value;23992400 2401 if new_owner_has_account {2402 2403 new_full_item2404 .owner2405 .iter_mut()2406 .find(|i| i.owner == new_owner)2407 .expect("new owner has account")2408 .fraction += value;2409 } else {2410 2411 new_full_item.owner.push(Ownership {2412 owner: new_owner.clone(),2413 fraction: value,2414 });2415 Self::add_token_index(collection_id, item_id, &new_owner)?;2416 }24172418 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2419 }24202421 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24222423 Ok(())2424 }24252426 fn transfer_nft(2427 collection: &CollectionHandle<T>,2428 item_id: TokenId,2429 sender: T::CrossAccountId,2430 new_owner: T::CrossAccountId,2431 ) -> DispatchResult {2432 let collection_id = collection.id;2433 let mut item = <NftItemList<T>>::get(collection_id, item_id)2434 .ok_or(Error::<T>::TokenNotFound)?;24352436 ensure!(2437 sender == item.owner,2438 Error::<T>::MustBeTokenOwner2439 );24402441 2442 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2443 .checked_sub(1)2444 .ok_or(Error::<T>::NumOverflow)?;2445 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24462447 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2448 .checked_add(1)2449 .ok_or(Error::<T>::NumOverflow)?;2450 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24512452 2453 let old_owner = item.owner.clone();2454 item.owner = new_owner.clone();2455 <NftItemList<T>>::insert(collection_id, item_id, item);24562457 2458 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24592460 collection.log(ERC721Events::Transfer {2461 from: *sender.as_eth(),2462 to: *new_owner.as_eth(),2463 token_id: item_id.into(),2464 });2465 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24662467 Ok(())2468 }2469 2470 fn set_re_fungible_variable_data(2471 collection: &CollectionHandle<T>,2472 item_id: TokenId,2473 data: Vec<u8>2474 ) -> DispatchResult {2475 let collection_id = collection.id;2476 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2477 .ok_or(Error::<T>::TokenNotFound)?;24782479 item.variable_data = data;24802481 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24822483 Ok(())2484 }24852486 fn set_nft_variable_data(2487 collection: &CollectionHandle<T>,2488 item_id: TokenId,2489 data: Vec<u8>2490 ) -> DispatchResult {2491 let collection_id = collection.id;2492 let mut item = <NftItemList<T>>::get(collection_id, item_id)2493 .ok_or(Error::<T>::TokenNotFound)?;2494 2495 item.variable_data = data;24962497 <NftItemList<T>>::insert(collection_id, item_id, item);2498 2499 Ok(())2500 }25012502 fn init_collection(item: &Collection<T>) {2503 2504 assert!(2505 item.decimal_points <= MAX_DECIMAL_POINTS,2506 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2507 );2508 assert!(2509 item.name.len() <= 64,2510 "Collection name can not be longer than 63 char"2511 );2512 assert!(2513 item.name.len() <= 256,2514 "Collection description can not be longer than 255 char"2515 );2516 assert!(2517 item.token_prefix.len() <= 16,2518 "Token prefix can not be longer than 15 char"2519 );25202521 2522 let next_id = CreatedCollectionCount::get()2523 .checked_add(1)2524 .unwrap();25252526 CreatedCollectionCount::put(next_id);2527 }25282529 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2530 let current_index = <ItemListIndex>::get(collection_id)2531 .checked_add(1)2532 .unwrap();25332534 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25352536 <ItemListIndex>::insert(collection_id, current_index);25372538 2539 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2540 .checked_add(1)2541 .unwrap();2542 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2543 }25442545 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2546 let current_index = <ItemListIndex>::get(collection_id)2547 .checked_add(1)2548 .unwrap();25492550 Self::add_token_index(collection_id, current_index, owner).unwrap();25512552 <ItemListIndex>::insert(collection_id, current_index);25532554 2555 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2556 .checked_add(item.value)2557 .unwrap();2558 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2559 }25602561 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2562 let current_index = <ItemListIndex>::get(collection_id)2563 .checked_add(1)2564 .unwrap();25652566 let value = item.owner.first().unwrap().fraction;2567 let owner = item.owner.first().unwrap().owner.clone();25682569 Self::add_token_index(collection_id, current_index, &owner).unwrap();25702571 <ItemListIndex>::insert(collection_id, current_index);25722573 2574 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2575 .checked_add(value)2576 .unwrap();2577 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2578 }25792580 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2581 2582 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {25832584 2585 let count = <AccountItemCount<T>>::get(owner.as_sub());2586 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25872588 <AccountItemCount<T>>::insert(owner.as_sub(), count2589 .checked_add(1)2590 .ok_or(Error::<T>::NumOverflow)?);2591 }2592 else {2593 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2594 }25952596 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2597 if list_exists {2598 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2599 let item_contains = list.contains(&item_index.clone());26002601 if !item_contains {2602 list.push(item_index.clone());2603 }26042605 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2606 } else {2607 let mut itm = Vec::new();2608 itm.push(item_index.clone());2609 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2610 }26112612 Ok(())2613 }26142615 fn remove_token_index(2616 collection_id: CollectionId,2617 item_index: TokenId,2618 owner: &T::CrossAccountId,2619 ) -> DispatchResult {26202621 2622 <AccountItemCount<T>>::insert(owner.as_sub(), 2623 <AccountItemCount<T>>::get(owner.as_sub())2624 .checked_sub(1)2625 .ok_or(Error::<T>::NumOverflow)?);262626272628 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2629 if list_exists {2630 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2631 let item_contains = list.contains(&item_index.clone());26322633 if item_contains {2634 list.retain(|&item| item != item_index);2635 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2636 }2637 }26382639 Ok(())2640 }26412642 fn move_token_index(2643 collection_id: CollectionId,2644 item_index: TokenId,2645 old_owner: &T::CrossAccountId,2646 new_owner: &T::CrossAccountId,2647 ) -> DispatchResult {2648 Self::remove_token_index(collection_id, item_index, old_owner)?;2649 Self::add_token_index(collection_id, item_index, new_owner)?;26502651 Ok(())2652 }2653 2654 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2655 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26562657 Ok(())2658 }2659}2660266126622663266426652666pub type Multiplier = FixedU128;26672668type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2669267026712672#[derive(Encode, Decode, Clone, Eq, PartialEq)]2673pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26742675impl<T: Config + Send + Sync> sp_std::fmt::Debug 2676 for ChargeTransactionPayment<T>2677{2678 #[cfg(feature = "std")]2679 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2680 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2681 }2682 #[cfg(not(feature = "std"))]2683 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2684 Ok(())2685 }2686}26872688impl<T: Config> ChargeTransactionPayment<T>2689where2690 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2691 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2692 T::AccountId: AsRef<[u8]>,2693 T::AccountId: UncheckedFrom<T::Hash>,2694{2695 fn traditional_fee(2696 len: usize,2697 info: &DispatchInfoOf<T::Call>,2698 tip: BalanceOf<T>,2699 ) -> BalanceOf<T>2700 where2701 T::Call: Dispatchable<Info = DispatchInfo>,2702 {2703 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2704 }27052706 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2707 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2708 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2709 let len_saturation = max_block_length as u64 / (len as u64).max(1);2710 let coefficient: BalanceOf<T> = weight_saturation2711 .min(len_saturation)2712 .saturated_into::<BalanceOf<T>>();2713 final_fee2714 .saturating_mul(coefficient)2715 .saturated_into::<TransactionPriority>()2716 }27172718 fn withdraw_fee(2719 &self,2720 who: &T::AccountId,2721 call: &T::Call,2722 info: &DispatchInfoOf<T::Call>,2723 len: usize,2724 ) -> Result<2725 (2726 BalanceOf<T>,2727 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2728 ),2729 TransactionValidityError,2730 > {2731 let tip = self.0;27322733 let fee = Self::traditional_fee(len, info, tip);27342735 2736 if fee.is_zero() {2737 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2738 .map(|i| (fee, i));2739 }27402741 2742 2743 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2744 Some(Call::create_item(collection_id, _owner, _properties)) => {2745 let collection = <CollectionById<T>>::get(collection_id)?;27462747 2748 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27492750 let limit = collection.limits.sponsor_transfer_timeout;2751 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2752 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2753 let limit_time = last_tx_block + limit.into();2754 if block_number <= limit_time {2755 return None;2756 }2757 }2758 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27592760 2761 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2762 collection.sponsorship.sponsor()2763 .cloned()2764 } else {2765 None2766 }2767 }2768 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2769 let collection = <CollectionById<T>>::get(collection_id)?;2770 2771 let mut sponsor_transfer = false;2772 if collection.sponsorship.confirmed() {27732774 let collection_limits = collection.limits;2775 let collection_mode = collection.mode;2776 2777 2778 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2779 sponsor_transfer = match collection_mode {2780 CollectionMode::NFT => {2781 2782 2783 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2784 collection_limits.sponsor_transfer_timeout2785 } else {2786 ChainLimit::get().nft_sponsor_transfer_timeout2787 };2788 2789 let mut sponsored = true;2790 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2791 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2792 let limit_time = last_tx_block + limit.into();2793 if block_number <= limit_time {2794 sponsored = false;2795 }2796 }2797 if sponsored {2798 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2799 }28002801 sponsored2802 }2803 CollectionMode::Fungible(_) => {2804 2805 2806 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2807 collection_limits.sponsor_transfer_timeout2808 } else {2809 ChainLimit::get().fungible_sponsor_transfer_timeout2810 };2811 2812 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2813 let mut sponsored = true;2814 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2815 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2816 let limit_time = last_tx_block + limit.into();2817 if block_number <= limit_time {2818 sponsored = false;2819 }2820 }2821 if sponsored {2822 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2823 }28242825 sponsored2826 }2827 CollectionMode::ReFungible => {2828 2829 2830 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2831 collection_limits.sponsor_transfer_timeout2832 } else {2833 ChainLimit::get().refungible_sponsor_transfer_timeout2834 };2835 2836 let mut sponsored = true;2837 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2838 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2839 let limit_time = last_tx_block + limit.into();2840 if block_number <= limit_time {2841 sponsored = false;2842 }2843 }2844 if sponsored {2845 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2846 }28472848 sponsored2849 }2850 _ => {2851 false2852 },2853 };2854 }28552856 if !sponsor_transfer {2857 None2858 } else {2859 collection.sponsorship.sponsor()2860 .cloned()2861 }2862 }28632864 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2865 let mut sponsor_metadata_changes = false;28662867 let collection = <CollectionById<T>>::get(collection_id)?;28682869 if2870 collection.sponsorship.confirmed() &&2871 2872 2873 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2874 data.len() <= collection.limits.sponsored_data_size as usize2875 {2876 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2877 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28782879 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2880 .map(|last_block| block_number - last_block > rate_limit)2881 .unwrap_or(true) 2882 {2883 sponsor_metadata_changes = true;2884 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2885 }2886 }2887 }28882889 if !sponsor_metadata_changes {2890 None2891 } else {2892 collection.sponsorship.sponsor().cloned()2893 }2894 }28952896 _ => None,2897 })();28982899 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2900 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29012902 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29032904 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2905 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2906 2907 if !owned_contract && white_list_enabled {2908 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2909 return Err(InvalidTransaction::Call.into());2910 }2911 }2912 },2913 _ => {},2914 }29152916 2917 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29182919 2920 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29212922 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2923 &who,2924 code_hash,2925 salt,2926 );2927 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29282929 None2930 },29312932 2933 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29342935 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2936 &who,2937 &T::Hashing::hash(&_code),2938 _salt,2939 );29402941 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29422943 None2944 }29452946 2947 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29482949 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29502951 let mut sponsor_transfer = false;2952 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2953 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2954 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2955 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2956 let limit_time = last_tx_block + rate_limit;29572958 if block_number >= limit_time {2959 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2960 sponsor_transfer = true;2961 }2962 } else {2963 sponsor_transfer = false;2964 }2965 2966 if sponsor_transfer {2967 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2968 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2969 return Some(called_contract);2970 }2971 }2972 }29732974 None2975 },29762977 _ => None,2978 });29792980 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29812982 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2983 .map(|i| (fee, i))2984 }2985}298629872988impl<T: Config + Send + Sync> SignedExtension2989 for ChargeTransactionPayment<T>2990where2991 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2992 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2993 T::AccountId: AsRef<[u8]>,2994 T::AccountId: UncheckedFrom<T::Hash>,2995{2996 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2997 type AccountId = T::AccountId;2998 type Call = T::Call;2999 type AdditionalSigned = ();3000 type Pre = (3001 3002 BalanceOf<T>,3003 3004 Self::AccountId,3005 3006 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3007 );3008 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3009 Ok(())3010 }30113012 fn validate(3013 &self,3014 who: &Self::AccountId,3015 call: &Self::Call,3016 info: &DispatchInfoOf<Self::Call>,3017 len: usize,3018 ) -> TransactionValidity {3019 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3020 Ok(ValidTransaction {3021 priority: Self::get_priority(len, info, fee),3022 ..Default::default()3023 })3024 }30253026 fn pre_dispatch(3027 self,3028 who: &Self::AccountId,3029 call: &Self::Call,3030 info: &DispatchInfoOf<Self::Call>,3031 len: usize,3032 ) -> Result<Self::Pre, TransactionValidityError> {3033 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3034 Ok((self.0, who.clone(), imbalance))3035 }30363037 fn post_dispatch(3038 pre: Self::Pre,3039 info: &DispatchInfoOf<Self::Call>,3040 post_info: &PostDispatchInfoOf<Self::Call>,3041 len: usize,3042 _result: &DispatchResult,3043 ) -> Result<(), TransactionValidityError> {3044 let (tip, who, imbalance) = pre;3045 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3046 len as u32,3047 info,3048 post_info,3049 tip,3050 );3051 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3052 Ok(())3053 }3054}3055305630573058sp_api::decl_runtime_apis! {3059 pub trait NftApi {3060 3061 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3062 }3063}