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_evm::AddressMapping;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}189190impl<T: Config> Deref for CollectionHandle<T> {191 type Target = Collection<T>;192193 fn deref(&self) -> &Self::Target {194 &self.collection195 }196}197198impl<T: Config> DerefMut for CollectionHandle<T> {199 fn deref_mut(&mut self) -> &mut Self::Target {200 &mut self.collection201 }202}203204#[derive(Encode, Decode, Debug, Clone, PartialEq)]205#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206pub struct NftItemType<AccountId> {207 pub owner: AccountId,208 pub const_data: Vec<u8>,209 pub variable_data: Vec<u8>,210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct FungibleItemType {215 pub value: u128,216}217218#[derive(Encode, Decode, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct ReFungibleItemType<AccountId> {221 pub owner: Vec<Ownership<AccountId>>,222 pub const_data: Vec<u8>,223 pub variable_data: Vec<u8>,224}225226227228229230231232233234235236237#[derive(Encode, Decode, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct CollectionLimits<BlockNumber: Encode + Decode> {240 pub account_token_ownership_limit: u32,241 pub sponsored_data_size: u32,242 243 244 245 pub sponsored_data_rate_limit: Option<BlockNumber>,246 pub token_limit: u32,247248 249 pub sponsor_transfer_timeout: u32,250 pub owner_can_transfer: bool,251 pub owner_can_destroy: bool,252}253254impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {255 fn default() -> Self {256 Self { 257 account_token_ownership_limit: 10_000_000, 258 token_limit: u32::max_value(),259 sponsored_data_size: u32::MAX, 260 sponsored_data_rate_limit: None,261 sponsor_transfer_timeout: 14400,262 owner_can_transfer: true,263 owner_can_destroy: true264 }265 }266}267268#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]269#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]270pub struct ChainLimits {271 pub collection_numbers_limit: u32,272 pub account_token_ownership_limit: u32,273 pub collections_admins_limit: u64,274 pub custom_data_limit: u32,275276 277 pub nft_sponsor_transfer_timeout: u32,278 pub fungible_sponsor_transfer_timeout: u32,279 pub refungible_sponsor_transfer_timeout: u32,280281 282 pub offchain_schema_limit: u32,283 pub variable_on_chain_schema_limit: u32,284 pub const_on_chain_schema_limit: u32,285}286287pub trait WeightInfo {288 fn create_collection() -> Weight;289 fn destroy_collection() -> Weight;290 fn add_to_white_list() -> Weight;291 fn remove_from_white_list() -> Weight;292 fn set_public_access_mode() -> Weight;293 fn set_mint_permission() -> Weight;294 fn change_collection_owner() -> Weight;295 fn add_collection_admin() -> Weight;296 fn remove_collection_admin() -> Weight;297 fn set_collection_sponsor() -> Weight;298 fn confirm_sponsorship() -> Weight;299 fn remove_collection_sponsor() -> Weight;300 fn create_item(s: usize) -> Weight;301 fn burn_item() -> Weight;302 fn transfer() -> Weight;303 fn approve() -> Weight;304 fn transfer_from() -> Weight;305 fn set_offchain_schema() -> Weight;306 fn set_const_on_chain_schema() -> Weight;307 fn set_variable_on_chain_schema() -> Weight;308 fn set_variable_meta_data() -> Weight;309 fn enable_contract_sponsoring() -> Weight;310 fn set_schema_version() -> Weight;311 fn set_chain_limits() -> Weight;312 fn set_contract_sponsoring_rate_limit() -> Weight;313 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;314 fn toggle_contract_white_list() -> Weight;315 fn add_to_contract_white_list() -> Weight;316 fn remove_from_contract_white_list() -> Weight;317 fn set_collection_limits() -> Weight;318}319320#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]321#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]322pub struct CreateNftData {323 pub const_data: Vec<u8>,324 pub variable_data: Vec<u8>,325}326327#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]328#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]329pub struct CreateFungibleData {330 pub value: u128,331}332333#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]334#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]335pub struct CreateReFungibleData {336 pub const_data: Vec<u8>,337 pub variable_data: Vec<u8>,338 pub pieces: u128,339}340341#[derive(Encode, Decode, Debug, Clone, PartialEq)]342#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]343pub enum CreateItemData {344 NFT(CreateNftData),345 Fungible(CreateFungibleData),346 ReFungible(CreateReFungibleData),347}348349impl CreateItemData {350 pub fn len(&self) -> usize {351 let len = match self {352 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),353 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),354 _ => 0355 };356 357 return len;358 }359}360361impl From<CreateNftData> for CreateItemData {362 fn from(item: CreateNftData) -> Self {363 CreateItemData::NFT(item)364 }365}366367impl From<CreateReFungibleData> for CreateItemData {368 fn from(item: CreateReFungibleData) -> Self {369 CreateItemData::ReFungible(item)370 }371}372373impl From<CreateFungibleData> for CreateItemData {374 fn from(item: CreateFungibleData) -> Self {375 CreateItemData::Fungible(item)376 }377}378379380decl_error! {381 382 pub enum Error for Module<T: Config> {383 384 TotalCollectionsLimitExceeded,385 386 CollectionDecimalPointLimitExceeded, 387 388 CollectionNameLimitExceeded, 389 390 CollectionDescriptionLimitExceeded, 391 392 CollectionTokenPrefixLimitExceeded,393 394 CollectionNotFound,395 396 TokenNotFound,397 398 AdminNotFound,399 400 NumOverflow, 401 402 AlreadyAdmin, 403 404 NoPermission,405 406 ConfirmUnsetSponsorFail,407 408 PublicMintingNotAllowed,409 410 MustBeTokenOwner,411 412 TokenValueTooLow,413 414 NftSizeLimitExceeded,415 416 ApproveNotFound,417 418 TokenValueNotEnough,419 420 ApproveRequired,421 422 AddresNotInWhiteList,423 424 CollectionAdminsLimitExceeded,425 426 AddressOwnershipLimitExceeded,427 428 EmptyArgument,429 430 TokenConstDataLimitExceeded,431 432 TokenVariableDataLimitExceeded,433 434 NotNftDataUsedToMintNftCollectionToken,435 436 NotFungibleDataUsedToMintFungibleCollectionToken,437 438 NotReFungibleDataUsedToMintReFungibleCollectionToken,439 440 UnexpectedCollectionType,441 442 CantStoreMetadataInFungibleTokens,443 444 CollectionTokenLimitExceeded,445 446 AccountTokenLimitExceeded,447 448 CollectionLimitBoundsExceeded,449 450 OwnerPermissionsCantBeReverted,451 452 SchemaDataLimitExceeded,453 454 WrongRefungiblePieces,455 456 BadCreateRefungibleCall,457 }458}459460pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {461 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;462463 464 type WeightInfo: WeightInfo;465466 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;467 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;468 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;469470 type CrossAccountId: CrossAccountId<Self::AccountId>;471 type Currency: Currency<Self::AccountId>;472 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;473 type TreasuryAccountId: Get<Self::AccountId>;474}475476#[cfg(feature = "runtime-benchmarks")]477mod benchmarking;478479480481482483484485486487488489490491492493494495496497498499500501502503decl_storage! {504 trait Store for Module<T: Config> as Nft {505506 507 508 CreatedCollectionCount: u32;509 510 ChainVersion: u64;511 512 513 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;514 515516 517 pub ChainLimit get(fn chain_limit) config(): ChainLimits;518 519520 521 522 523 DestroyedCollectionCount: u32;524 525 526 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;527 528529 530 531 532 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;533 534 535 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;536 537 538 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;539 540541 542 543 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;544545 546 547 548 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;549550 551 552 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;553 554 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;555 556 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;557 558559 560 561 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;562 563564 565 566 567 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;568 569 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;570 571 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;572 573 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;574 575576 577 578 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;579 580 581 582 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;583 584 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;585 586 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;587 588 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;589 590 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 591 592 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 593 594 }595 add_extra_genesis {596 build(|config: &GenesisConfig<T>| {597 598 for (_num, _c) in &config.collection_id {599 <Module<T>>::init_collection(_c);600 }601602 for (_num, _c, _i) in &config.nft_item_id {603 <Module<T>>::init_nft_token(*_c, _i);604 }605606 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {607 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);608 }609610 for (_num, _c, _i) in &config.refungible_item_id {611 <Module<T>>::init_refungible_token(*_c, _i);612 }613 })614 }615}616617decl_event!(618 pub enum Event<T>619 where620 CrossAccountId = <T as Config>::CrossAccountId,621 {622 623 624 625 626 627 628 629 630 631 CollectionCreated(CollectionId, u8, CrossAccountId),632633 634 635 636 637 638 639 640 641 642 ItemCreated(CollectionId, TokenId, CrossAccountId),643644 645 646 647 648 649 650 651 ItemDestroyed(CollectionId, TokenId),652653 654 655 656 657 658 659 660 661 662 663 664 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),665666 667 668 669 670 671 672 673 674 675 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),676 }677);678679decl_module! {680 pub struct Module<T: Config> for enum Call 681 where 682 origin: T::Origin683 {684 fn deposit_event() = default;685 type Error = Error<T>;686687 fn on_initialize(now: T::BlockNumber) -> Weight {688 0689 }690691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 #[weight = <T as Config>::WeightInfo::create_collection()]708 #[transactional]709 pub fn create_collection(origin,710 collection_name: Vec<u16>,711 collection_description: Vec<u16>,712 token_prefix: Vec<u8>,713 mode: CollectionMode) -> DispatchResult {714715 716 let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);717718 719 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();720 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(721 &T::TreasuryAccountId::get(),722 T::CollectionCreationPrice::get(),723 ));724 <T as Config>::Currency::settle(725 who.as_sub(),726 imbalance,727 WithdrawReasons::TRANSFER,728 ExistenceRequirement::KeepAlive,729 ).map_err(|_| Error::<T>::NoPermission)?;730731 let decimal_points = match mode {732 CollectionMode::Fungible(points) => points,733 _ => 0734 };735736 let chain_limit = ChainLimit::get();737738 let created_count = CreatedCollectionCount::get();739 let destroyed_count = DestroyedCollectionCount::get();740741 742 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);743744 745 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);746 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);747 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);748 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);749750 751 let next_id = created_count752 .checked_add(1)753 .ok_or(Error::<T>::NumOverflow)?;754755 CreatedCollectionCount::put(next_id);756757 let limits = CollectionLimits {758 sponsored_data_size: chain_limit.custom_data_limit,759 ..Default::default()760 };761762 763 let new_collection = Collection {764 owner: who.clone(),765 name: collection_name,766 mode: mode.clone(),767 mint_mode: false,768 access: AccessMode::Normal,769 description: collection_description,770 decimal_points: decimal_points,771 token_prefix: token_prefix,772 offchain_schema: Vec::new(),773 schema_version: SchemaVersion::ImageURL,774 sponsorship: SponsorshipState::Disabled,775 variable_on_chain_schema: Vec::new(),776 const_on_chain_schema: Vec::new(),777 limits,778 };779780 781 <CollectionById<T>>::insert(next_id, new_collection);782783 784 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));785786 Ok(())787 }788789 790 791 792 793 794 795 796 797 798 #[weight = <T as Config>::WeightInfo::destroy_collection()]799 #[transactional]800 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {801802 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);803 let collection = Self::get_collection(collection_id)?;804 Self::check_owner_permissions(&collection, &sender)?;805 if !collection.limits.owner_can_destroy {806 fail!(Error::<T>::NoPermission);807 }808809 <AddressTokens<T>>::remove_prefix(collection_id);810 <Allowances<T>>::remove_prefix(collection_id);811 <Balance<T>>::remove_prefix(collection_id);812 <ItemListIndex>::remove(collection_id);813 <AdminList<T>>::remove(collection_id);814 <CollectionById<T>>::remove(collection_id);815 <WhiteList<T>>::remove_prefix(collection_id);816817 <NftItemList<T>>::remove_prefix(collection_id);818 <FungibleItemList<T>>::remove_prefix(collection_id);819 <ReFungibleItemList<T>>::remove_prefix(collection_id);820821 <NftTransferBasket<T>>::remove_prefix(collection_id);822 <FungibleTransferBasket<T>>::remove_prefix(collection_id);823 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);824825 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);826827 DestroyedCollectionCount::put(DestroyedCollectionCount::get()828 .checked_add(1)829 .ok_or(Error::<T>::NumOverflow)?);830831 Ok(())832 }833834 835 836 837 838 839 840 841 842 843 844 845 846 #[weight = <T as Config>::WeightInfo::add_to_white_list()]847 #[transactional]848 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{849850 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851 let collection = Self::get_collection(collection_id)?;852 Self::check_owner_or_admin_permissions(&collection, &sender)?;853854 <WhiteList<T>>::insert(collection_id, address.as_sub(), true);855 856 Ok(())857 }858859 860 861 862 863 864 865 866 867 868 869 870 871 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]872 #[transactional]873 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{874875 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);876 let collection = Self::get_collection(collection_id)?;877 Self::check_owner_or_admin_permissions(&collection, &sender)?;878879 <WhiteList<T>>::remove(collection_id, address.as_sub());880881 Ok(())882 }883884 885 886 887 888 889 890 891 892 893 894 895 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]896 #[transactional]897 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult898 {899 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900901 let mut target_collection = Self::get_collection(collection_id)?;902 Self::check_owner_permissions(&target_collection, &sender)?;903 target_collection.access = mode;904 Self::save_collection(target_collection);905906 Ok(())907 }908909 910 911 912 913 914 915 916 917 918 919 920 921 922 #[weight = <T as Config>::WeightInfo::set_mint_permission()]923 #[transactional]924 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult925 {926 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);927928 let mut target_collection = Self::get_collection(collection_id)?;929 Self::check_owner_permissions(&target_collection, &sender)?;930 target_collection.mint_mode = mint_permission;931 Self::save_collection(target_collection);932933 Ok(())934 }935936 937 938 939 940 941 942 943 944 945 946 947 #[weight = <T as Config>::WeightInfo::change_collection_owner()]948 #[transactional]949 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {950951 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952 let mut target_collection = Self::get_collection(collection_id)?;953 Self::check_owner_permissions(&target_collection, &sender)?;954 target_collection.owner = new_owner;955 Self::save_collection(target_collection);956957 Ok(())958 }959960 961 962 963 964 965 966 967 968 969 970 971 972 973 #[weight = <T as Config>::WeightInfo::add_collection_admin()]974 #[transactional]975 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {976 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);977 let collection = Self::get_collection(collection_id)?;978 Self::check_owner_or_admin_permissions(&collection, &sender)?;979 let mut admin_arr = <AdminList<T>>::get(collection_id);980981 match admin_arr.binary_search(&new_admin_id) {982 Ok(_) => {},983 Err(idx) => {984 let limits = ChainLimit::get();985 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);986 admin_arr.insert(idx, new_admin_id);987 <AdminList<T>>::insert(collection_id, admin_arr);988 }989 }990 Ok(())991 }992993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1006 #[transactional]1007 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1008 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1009 let collection = Self::get_collection(collection_id)?;1010 Self::check_owner_or_admin_permissions(&collection, &sender)?;1011 let mut admin_arr = <AdminList<T>>::get(collection_id);10121013 match admin_arr.binary_search(&account_id) {1014 Ok(idx) => {1015 admin_arr.remove(idx);1016 <AdminList<T>>::insert(collection_id, admin_arr);1017 },1018 Err(_) => {}1019 }1020 Ok(())1021 }10221023 1024 1025 1026 1027 1028 1029 1030 1031 1032 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1033 #[transactional]1034 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1035 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036 let mut target_collection = Self::get_collection(collection_id)?;1037 Self::check_owner_permissions(&target_collection, &sender)?;10381039 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1040 Self::save_collection(target_collection);10411042 Ok(())1043 }10441045 1046 1047 1048 1049 1050 1051 1052 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1053 #[transactional]1054 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1055 let sender = ensure_signed(origin)?;10561057 let mut target_collection = Self::get_collection(collection_id)?;1058 ensure!(1059 target_collection.sponsorship.pending_sponsor() == Some(&sender),1060 Error::<T>::ConfirmUnsetSponsorFail1061 );10621063 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1064 Self::save_collection(target_collection);10651066 Ok(())1067 }10681069 1070 1071 1072 1073 1074 1075 1076 1077 1078 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1079 #[transactional]1080 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1081 let sender = ensure_signed(origin)?;10821083 let mut target_collection = Self::get_collection(collection_id)?;1084 Self::check_owner_permissions(&target_collection, &T::CrossAccountId::from_sub(sender))?;10851086 target_collection.sponsorship = SponsorshipState::Disabled;1087 Self::save_collection(target_collection);10881089 Ok(())1090 }10911092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 11151116 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1117 #[transactional]1118 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {11191120 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11211122 let target_collection = Self::get_collection(collection_id)?;11231124 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1125 Self::validate_create_item_args(&target_collection, &data)?;1126 Self::create_item_no_validation(&target_collection, owner, data)?;11271128 Ok(())1129 }11301131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1150 .map(|data| { data.len() })1151 .sum())]1152 #[transactional]1153 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11541155 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1156 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1157 let collection = Self::get_collection(collection_id)?;11581159 Self::create_multiple_items_internal(sender, &collection, owner, items_data)?;11601161 Ok(())1162 }11631164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 #[weight = <T as Config>::WeightInfo::burn_item()]1178 #[transactional]1179 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11801181 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1182 let target_collection = Self::get_collection(collection_id)?;11831184 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;11851186 Ok(())1187 }11881189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 #[weight = <T as Config>::WeightInfo::transfer()]1213 #[transactional]1214 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1215 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1216 let collection = Self::get_collection(collection_id)?;12171218 Self::transfer_internal(sender, recipient, &collection, item_id, value)?;12191220 Ok(())1221 }12221223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 #[weight = <T as Config>::WeightInfo::approve()]1239 #[transactional]1240 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12411242 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1243 let collection = Self::get_collection(collection_id)?;12441245 Self::approve_internal(sender, spender, &collection, item_id, amount)?;12461247 Ok(())1248 }1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 #[weight = <T as Config>::WeightInfo::transfer_from()]1270 #[transactional]1271 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12721273 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1274 let collection = Self::get_collection(collection_id)?;12751276 Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?;12771278 Ok(())1279 }12801281 1282 12831284 1285 1286 1287 12881289 12901291 12921293 1294 12951296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1309 #[transactional]1310 pub fn set_variable_meta_data (1311 origin,1312 collection_id: CollectionId,1313 item_id: TokenId,1314 data: Vec<u8>1315 ) -> DispatchResult {1316 let sender = ensure_signed(origin)?;1317 1318 let target_collection = Self::get_collection(collection_id)?;1319 Self::token_exists(&target_collection, item_id)?;13201321 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13221323 1324 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1325 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1326 Error::<T>::NoPermission);13271328 match target_collection.mode1329 {1330 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1331 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1332 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1333 _ => fail!(Error::<T>::UnexpectedCollectionType)1334 };13351336 Ok(())1337 }1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 #[weight = <T as Config>::WeightInfo::set_schema_version()]1354 #[transactional]1355 pub fn set_schema_version(1356 origin,1357 collection_id: CollectionId,1358 version: SchemaVersion1359 ) -> DispatchResult {1360 let sender = ensure_signed(origin)?;1361 let mut target_collection = Self::get_collection(collection_id)?;1362 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1363 target_collection.schema_version = version;1364 Self::save_collection(target_collection);13651366 Ok(())1367 }13681369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1382 #[transactional]1383 pub fn set_offchain_schema(1384 origin,1385 collection_id: CollectionId,1386 schema: Vec<u8>1387 ) -> DispatchResult {1388 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1389 let mut target_collection = Self::get_collection(collection_id)?;1390 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;13911392 1393 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13941395 target_collection.offchain_schema = schema;1396 Self::save_collection(target_collection);13971398 Ok(())1399 }14001401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1414 #[transactional]1415 pub fn set_const_on_chain_schema (1416 origin,1417 collection_id: CollectionId,1418 schema: Vec<u8>1419 ) -> DispatchResult {1420 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1421 let mut target_collection = Self::get_collection(collection_id)?;1422 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14231424 1425 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14261427 target_collection.const_on_chain_schema = schema;1428 Self::save_collection(target_collection);14291430 Ok(())1431 }14321433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1446 #[transactional]1447 pub fn set_variable_on_chain_schema (1448 origin,1449 collection_id: CollectionId,1450 schema: Vec<u8>1451 ) -> DispatchResult {1452 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1453 let mut target_collection = Self::get_collection(collection_id)?;1454 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14551456 1457 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14581459 target_collection.variable_on_chain_schema = schema;1460 Self::save_collection(target_collection);14611462 Ok(())1463 }14641465 1466 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1467 #[transactional]1468 pub fn set_chain_limits(1469 origin,1470 limits: ChainLimits1471 ) -> DispatchResult {14721473 #[cfg(not(feature = "runtime-benchmarks"))]1474 ensure_root(origin)?;14751476 <ChainLimit>::put(limits);1477 Ok(())1478 }14791480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1492 #[transactional]1493 pub fn enable_contract_sponsoring(1494 origin,1495 contract_address: T::AccountId,1496 enable: bool1497 ) -> DispatchResult {14981499 let sender = ensure_signed(origin)?;15001501 #[cfg(feature = "runtime-benchmarks")]1502 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15031504 Self::ensure_contract_owned(sender, &contract_address)?;15051506 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1507 Ok(())1508 }15091510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1528 #[transactional]1529 pub fn set_contract_sponsoring_rate_limit(1530 origin,1531 contract_address: T::AccountId,1532 rate_limit: T::BlockNumber1533 ) -> DispatchResult {1534 let sender = ensure_signed(origin)?;15351536 #[cfg(feature = "runtime-benchmarks")]1537 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15381539 Self::ensure_contract_owned(sender, &contract_address)?;1540 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1541 Ok(())1542 }15431544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1556 #[transactional]1557 pub fn toggle_contract_white_list(1558 origin,1559 contract_address: T::AccountId,1560 enable: bool1561 ) -> DispatchResult {1562 let sender = ensure_signed(origin)?;15631564 #[cfg(feature = "runtime-benchmarks")]1565 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15661567 Self::ensure_contract_owned(sender, &contract_address)?;1568 if enable {1569 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1570 } else {1571 <ContractWhiteListEnabled<T>>::remove(contract_address);1572 }1573 Ok(())1574 }1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1588 #[transactional]1589 pub fn add_to_contract_white_list(1590 origin,1591 contract_address: T::AccountId,1592 account_address: T::AccountId1593 ) -> DispatchResult {1594 let sender = ensure_signed(origin)?;15951596 #[cfg(feature = "runtime-benchmarks")]1597 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1598 1599 Self::ensure_contract_owned(sender, &contract_address)?; 1600 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1601 Ok(())1602 }16031604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1616 #[transactional]1617 pub fn remove_from_contract_white_list(1618 origin,1619 contract_address: T::AccountId,1620 account_address: T::AccountId1621 ) -> DispatchResult {1622 let sender = ensure_signed(origin)?;16231624 #[cfg(feature = "runtime-benchmarks")]1625 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16261627 Self::ensure_contract_owned(sender, &contract_address)?;1628 <ContractWhiteList<T>>::remove(contract_address, account_address);1629 Ok(())1630 }16311632 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1633 #[transactional]1634 pub fn set_collection_limits(1635 origin,1636 collection_id: u32,1637 new_limits: CollectionLimits<T::BlockNumber>,1638 ) -> DispatchResult {1639 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1640 let mut target_collection = Self::get_collection(collection_id)?;1641 Self::check_owner_permissions(&target_collection, &sender)?;1642 let old_limits = &target_collection.limits;1643 let chain_limits = ChainLimit::get();16441645 1646 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1647 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1648 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1649 Error::<T>::CollectionLimitBoundsExceeded);16501651 1652 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1653 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16541655 ensure!(1656 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1657 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1658 Error::<T>::OwnerPermissionsCantBeReverted,1659 );16601661 target_collection.limits = new_limits;1662 Self::save_collection(target_collection);16631664 Ok(())1665 } 1666 }1667}16681669impl<T: Config> Module<T> {16701671 pub fn transfer_internal(sender: T::CrossAccountId, recipient: T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1672 1673 Self::is_correct_transfer(target_collection, &recipient)?;16741675 1676 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1677 Self::is_owner_or_admin_permissions(target_collection, &sender),1678 Error::<T>::NoPermission);16791680 if target_collection.access == AccessMode::WhiteList {1681 Self::check_white_list(target_collection, &sender)?;1682 Self::check_white_list(target_collection, &recipient)?;1683 }16841685 match target_collection.mode1686 {1687 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1688 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1689 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1690 _ => ()1691 };16921693 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));16941695 Ok(())1696 }16971698 pub fn approve_internal(1699 sender: T::CrossAccountId,1700 spender: T::CrossAccountId,1701 collection: &CollectionHandle<T>,1702 item_id: TokenId,1703 amount: u1281704 ) -> DispatchResult {1705 Self::token_exists(&collection, item_id)?;17061707 1708 let bypasses_limits = collection.limits.owner_can_transfer &&1709 Self::is_owner_or_admin_permissions(1710 &collection,1711 &sender,1712 );17131714 let allowance_limit = if bypasses_limits {1715 None1716 } else if let Some(amount) = Self::owned_amount(1717 &sender,1718 &collection,1719 item_id,1720 ) {1721 Some(amount)1722 } else {1723 fail!(Error::<T>::NoPermission);1724 };17251726 if collection.access == AccessMode::WhiteList {1727 Self::check_white_list(&collection, &sender)?;1728 Self::check_white_list(&collection, &spender)?;1729 }17301731 let allowance: u128 = amount1732 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1733 .ok_or(Error::<T>::NumOverflow)?;1734 if let Some(limit) = allowance_limit {1735 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1736 }1737 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);173817391740 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1741 Ok(())1742 }17431744 pub fn transfer_from_internal(1745 sender: T::CrossAccountId,1746 from: T::CrossAccountId,1747 recipient: T::CrossAccountId,1748 collection: &CollectionHandle<T>,1749 item_id: TokenId,1750 amount: u128,1751 ) -> DispatchResult {1752 1753 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17541755 1756 Self::is_correct_transfer(&collection, &recipient)?;17571758 1759 ensure!(1760 approval >= amount || 1761 (1762 collection.limits.owner_can_transfer &&1763 Self::is_owner_or_admin_permissions(&collection, &sender)1764 ),1765 Error::<T>::NoPermission1766 );17671768 if collection.access == AccessMode::WhiteList {1769 Self::check_white_list(&collection, &sender)?;1770 Self::check_white_list(&collection, &recipient)?;1771 }17721773 1774 let allowance = approval.saturating_sub(amount);1775 if allowance > 0 {1776 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1777 } else {1778 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1779 }17801781 match collection.mode {1782 CollectionMode::NFT => {1783 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1784 }1785 CollectionMode::Fungible(_) => {1786 Self::transfer_fungible(&collection, amount, &from, &recipient)?1787 }1788 CollectionMode::ReFungible => {1789 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1790 }1791 _ => ()1792 };17931794 Ok(())1795 }17961797 pub fn create_multiple_items_internal(1798 sender: T::CrossAccountId,1799 collection: &CollectionHandle<T>,1800 owner: T::CrossAccountId,1801 items_data: Vec<CreateItemData>,1802 ) -> DispatchResult {1803 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18041805 for data in &items_data {1806 Self::validate_create_item_args(&collection, data)?;1807 }1808 for data in &items_data {1809 Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1810 }18111812 Ok(())1813 }18141815 pub fn burn_item_internal(1816 sender: &T::CrossAccountId,1817 collection: &CollectionHandle<T>,1818 item_id: TokenId,1819 value: u128,1820 ) -> DispatchResult {1821 ensure!(1822 Self::is_item_owner(&sender, &collection, item_id) ||1823 (1824 collection.limits.owner_can_transfer &&1825 Self::is_owner_or_admin_permissions(&collection, &sender)1826 ),1827 Error::<T>::NoPermission1828 );18291830 if collection.access == AccessMode::WhiteList {1831 Self::check_white_list(&collection, &sender)?;1832 }18331834 match collection.mode1835 {1836 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1837 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1838 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1839 _ => ()1840 };18411842 Ok(())1843 }18441845 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1846 let collection_id = collection.id;18471848 1849 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1850 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1851 1852 Ok(())1853 }18541855 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1856 let collection_id = collection.id;18571858 1859 let total_items: u32 = ItemListIndex::get(collection_id)1860 .checked_add(amount)1861 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1862 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1863 .checked_add(amount)1864 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1865 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1866 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);18671868 if !Self::is_owner_or_admin_permissions(collection, &sender) {1869 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1870 Self::check_white_list(collection, owner)?;1871 Self::check_white_list(collection, sender)?;1872 }18731874 Ok(())1875 }18761877 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1878 match target_collection.mode1879 {1880 CollectionMode::NFT => {1881 if let CreateItemData::NFT(data) = data {1882 1883 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1884 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1885 } else {1886 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1887 }1888 },1889 CollectionMode::Fungible(_) => {1890 if let CreateItemData::Fungible(_) = data {1891 } else {1892 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1893 }1894 },1895 CollectionMode::ReFungible => {1896 if let CreateItemData::ReFungible(data) = data {18971898 1899 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1900 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19011902 1903 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1904 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1905 } else {1906 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1907 }1908 },1909 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1910 };19111912 Ok(())1913 }19141915 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1916 match data1917 {1918 CreateItemData::NFT(data) => {1919 let item = NftItemType {1920 owner: owner.clone(),1921 const_data: data.const_data,1922 variable_data: data.variable_data1923 };19241925 Self::add_nft_item(collection, item)?;1926 },1927 CreateItemData::Fungible(data) => {1928 Self::add_fungible_item(collection, &owner, data.value)?;1929 },1930 CreateItemData::ReFungible(data) => {1931 let mut owner_list = Vec::new();1932 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});19331934 let item = ReFungibleItemType {1935 owner: owner_list,1936 const_data: data.const_data,1937 variable_data: data.variable_data1938 };19391940 Self::add_refungible_item(collection, item)?;1941 }1942 };19431944 Ok(())1945 }19461947 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1948 let collection_id = collection.id;19491950 1951 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;19521953 1954 let item = FungibleItemType {1955 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1956 };1957 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);19581959 1960 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1961 .checked_add(value)1962 .ok_or(Error::<T>::NumOverflow)?;1963 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19641965 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1966 Ok(())1967 }19681969 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1970 let collection_id = collection.id;19711972 let current_index = <ItemListIndex>::get(collection_id)1973 .checked_add(1)1974 .ok_or(Error::<T>::NumOverflow)?;1975 let itemcopy = item.clone();19761977 ensure!(1978 item.owner.len() == 1,1979 Error::<T>::BadCreateRefungibleCall,1980 );1981 let item_owner = item.owner.first().expect("only one owner is defined");19821983 let value = item_owner.fraction;1984 let owner = item_owner.owner.clone();19851986 Self::add_token_index(collection_id, current_index, &owner)?;19871988 <ItemListIndex>::insert(collection_id, current_index);1989 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);19901991 1992 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1993 .checked_add(value)1994 .ok_or(Error::<T>::NumOverflow)?;1995 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19961997 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1998 Ok(())1999 }20002001 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2002 let collection_id = collection.id;20032004 let current_index = <ItemListIndex>::get(collection_id)2005 .checked_add(1)2006 .ok_or(Error::<T>::NumOverflow)?;20072008 let item_owner = item.owner.clone();2009 Self::add_token_index(collection_id, current_index, &item.owner)?;20102011 <ItemListIndex>::insert(collection_id, current_index);2012 <NftItemList<T>>::insert(collection_id, current_index, item);20132014 2015 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2016 .checked_add(1)2017 .ok_or(Error::<T>::NumOverflow)?;2018 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);20192020 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2021 Ok(())2022 }20232024 fn burn_refungible_item(2025 collection: &CollectionHandle<T>,2026 item_id: TokenId,2027 owner: &T::CrossAccountId,2028 ) -> DispatchResult {2029 let collection_id = collection.id;20302031 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2032 .ok_or(Error::<T>::TokenNotFound)?;2033 let rft_balance = token2034 .owner2035 .iter()2036 .find(|&i| i.owner == *owner)2037 .ok_or(Error::<T>::TokenNotFound)?;2038 Self::remove_token_index(collection_id, item_id, owner)?;20392040 2041 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2042 .checked_sub(rft_balance.fraction)2043 .ok_or(Error::<T>::NumOverflow)?;2044 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);20452046 2047 let index = token2048 .owner2049 .iter()2050 .position(|i| i.owner == *owner)2051 .expect("owned item is exists");2052 token.owner.remove(index);2053 let owner_count = token.owner.len();20542055 2056 if owner_count == 0 {2057 <ReFungibleItemList<T>>::remove(collection_id, item_id);2058 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2059 }2060 else {2061 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2062 }20632064 Ok(())2065 }20662067 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2068 let collection_id = collection.id;20692070 let item = <NftItemList<T>>::get(collection_id, item_id)2071 .ok_or(Error::<T>::TokenNotFound)?;2072 Self::remove_token_index(collection_id, item_id, &item.owner)?;20732074 2075 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2076 .checked_sub(1)2077 .ok_or(Error::<T>::NumOverflow)?;2078 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2079 <NftItemList<T>>::remove(collection_id, item_id);2080 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);20812082 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2083 Ok(())2084 }20852086 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2087 let collection_id = collection.id;20882089 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2090 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);20912092 2093 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2094 .checked_sub(value)2095 .ok_or(Error::<T>::NumOverflow)?;2096 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20972098 if balance.value - value > 0 {2099 balance.value -= value;2100 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2101 }2102 else {2103 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2104 }21052106 Ok(())2107 }21082109 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2110 Ok(<CollectionById<T>>::get(collection_id)2111 .map(|collection| CollectionHandle {2112 id: collection_id,2113 collection2114 })2115 .ok_or(Error::<T>::CollectionNotFound)?)2116 }21172118 fn save_collection(collection: CollectionHandle<T>) {2119 <CollectionById<T>>::insert(collection.id, collection.collection);2120 }212121222123 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2124 ensure!(2125 *subject == target_collection.owner,2126 Error::<T>::NoPermission2127 );21282129 Ok(())2130 }21312132 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2133 *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2134 }21352136 fn check_owner_or_admin_permissions(2137 collection: &CollectionHandle<T>,2138 subject: &T::CrossAccountId,2139 ) -> DispatchResult {2140 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);21412142 Ok(())2143 }21442145 fn owned_amount(2146 subject: &T::CrossAccountId,2147 target_collection: &CollectionHandle<T>,2148 item_id: TokenId,2149 ) -> Option<u128> {2150 let collection_id = target_collection.id;21512152 match target_collection.mode {2153 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2154 .then(|| 1),2155 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2156 .value),2157 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2158 .owner2159 .iter()2160 .find(|i| i.owner == *subject)2161 .map(|i| i.fraction),2162 CollectionMode::Invalid => None,2163 }2164 }21652166 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2167 match target_collection.mode {2168 CollectionMode::Fungible(_) => true,2169 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2170 }2171 }21722173 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2174 let collection_id = collection.id;21752176 let mes = Error::<T>::AddresNotInWhiteList;2177 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);21782179 Ok(())2180 }21812182 2183 2184 fn token_exists(2185 target_collection: &CollectionHandle<T>,2186 item_id: TokenId,2187 ) -> DispatchResult {2188 let collection_id = target_collection.id;2189 let exists = match target_collection.mode2190 {2191 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2192 CollectionMode::Fungible(_) => true,2193 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2194 _ => false2195 };21962197 ensure!(exists == true, Error::<T>::TokenNotFound);2198 Ok(())2199 }22002201 fn transfer_fungible(2202 collection: &CollectionHandle<T>,2203 value: u128,2204 owner: &T::CrossAccountId,2205 recipient: &T::CrossAccountId,2206 ) -> DispatchResult {2207 let collection_id = collection.id;22082209 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2210 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);22112212 2213 Self::add_fungible_item(collection, recipient, value)?;22142215 2216 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);22172218 2219 if balance.value == value {2220 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2221 }2222 else {2223 balance.value -= value;2224 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2225 }22262227 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));22282229 Ok(())2230 }22312232 fn transfer_refungible(2233 collection: &CollectionHandle<T>,2234 item_id: TokenId,2235 value: u128,2236 owner: T::CrossAccountId,2237 new_owner: T::CrossAccountId,2238 ) -> DispatchResult {2239 let collection_id = collection.id;2240 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2241 .ok_or(Error::<T>::TokenNotFound)?;22422243 let item = full_item2244 .owner2245 .iter()2246 .filter(|i| i.owner == owner)2247 .next()2248 .ok_or(Error::<T>::TokenNotFound)?;2249 let amount = item.fraction;22502251 ensure!(amount >= value, Error::<T>::TokenValueTooLow);22522253 2254 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2255 .checked_sub(value)2256 .ok_or(Error::<T>::NumOverflow)?;2257 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);22582259 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2260 .checked_add(value)2261 .ok_or(Error::<T>::NumOverflow)?;2262 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);22632264 let old_owner = item.owner.clone();2265 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);22662267 2268 if amount == value && !new_owner_has_account {2269 2270 2271 let mut new_full_item = full_item.clone();2272 new_full_item2273 .owner2274 .iter_mut()2275 .find(|i| i.owner == owner)2276 .expect("old owner does present in refungible")2277 .owner = new_owner.clone();2278 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);22792280 2281 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2282 } else {2283 let mut new_full_item = full_item.clone();2284 new_full_item2285 .owner2286 .iter_mut()2287 .find(|i| i.owner == owner)2288 .expect("old owner does present in refungible")2289 .fraction -= value;22902291 2292 if new_owner_has_account {2293 2294 new_full_item2295 .owner2296 .iter_mut()2297 .find(|i| i.owner == new_owner)2298 .expect("new owner has account")2299 .fraction += value;2300 } else {2301 2302 new_full_item.owner.push(Ownership {2303 owner: new_owner.clone(),2304 fraction: value,2305 });2306 Self::add_token_index(collection_id, item_id, &new_owner)?;2307 }23082309 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2310 }23112312 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));23132314 Ok(())2315 }23162317 fn transfer_nft(2318 collection: &CollectionHandle<T>,2319 item_id: TokenId,2320 sender: T::CrossAccountId,2321 new_owner: T::CrossAccountId,2322 ) -> DispatchResult {2323 let collection_id = collection.id;2324 let mut item = <NftItemList<T>>::get(collection_id, item_id)2325 .ok_or(Error::<T>::TokenNotFound)?;23262327 ensure!(2328 sender == item.owner,2329 Error::<T>::MustBeTokenOwner2330 );23312332 2333 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2334 .checked_sub(1)2335 .ok_or(Error::<T>::NumOverflow)?;2336 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23372338 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2339 .checked_add(1)2340 .ok_or(Error::<T>::NumOverflow)?;2341 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23422343 2344 let old_owner = item.owner.clone();2345 item.owner = new_owner.clone();2346 <NftItemList<T>>::insert(collection_id, item_id, item);23472348 2349 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;23502351 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));23522353 Ok(())2354 }2355 2356 fn set_re_fungible_variable_data(2357 collection: &CollectionHandle<T>,2358 item_id: TokenId,2359 data: Vec<u8>2360 ) -> DispatchResult {2361 let collection_id = collection.id;2362 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2363 .ok_or(Error::<T>::TokenNotFound)?;23642365 item.variable_data = data;23662367 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);23682369 Ok(())2370 }23712372 fn set_nft_variable_data(2373 collection: &CollectionHandle<T>,2374 item_id: TokenId,2375 data: Vec<u8>2376 ) -> DispatchResult {2377 let collection_id = collection.id;2378 let mut item = <NftItemList<T>>::get(collection_id, item_id)2379 .ok_or(Error::<T>::TokenNotFound)?;2380 2381 item.variable_data = data;23822383 <NftItemList<T>>::insert(collection_id, item_id, item);2384 2385 Ok(())2386 }23872388 fn init_collection(item: &Collection<T>) {2389 2390 assert!(2391 item.decimal_points <= MAX_DECIMAL_POINTS,2392 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2393 );2394 assert!(2395 item.name.len() <= 64,2396 "Collection name can not be longer than 63 char"2397 );2398 assert!(2399 item.name.len() <= 256,2400 "Collection description can not be longer than 255 char"2401 );2402 assert!(2403 item.token_prefix.len() <= 16,2404 "Token prefix can not be longer than 15 char"2405 );24062407 2408 let next_id = CreatedCollectionCount::get()2409 .checked_add(1)2410 .unwrap();24112412 CreatedCollectionCount::put(next_id);2413 }24142415 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2416 let current_index = <ItemListIndex>::get(collection_id)2417 .checked_add(1)2418 .unwrap();24192420 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();24212422 <ItemListIndex>::insert(collection_id, current_index);24232424 2425 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2426 .checked_add(1)2427 .unwrap();2428 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2429 }24302431 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2432 let current_index = <ItemListIndex>::get(collection_id)2433 .checked_add(1)2434 .unwrap();24352436 Self::add_token_index(collection_id, current_index, owner).unwrap();24372438 <ItemListIndex>::insert(collection_id, current_index);24392440 2441 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2442 .checked_add(item.value)2443 .unwrap();2444 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2445 }24462447 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2448 let current_index = <ItemListIndex>::get(collection_id)2449 .checked_add(1)2450 .unwrap();24512452 let value = item.owner.first().unwrap().fraction;2453 let owner = item.owner.first().unwrap().owner.clone();24542455 Self::add_token_index(collection_id, current_index, &owner).unwrap();24562457 <ItemListIndex>::insert(collection_id, current_index);24582459 2460 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2461 .checked_add(value)2462 .unwrap();2463 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2464 }24652466 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2467 2468 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {24692470 2471 let count = <AccountItemCount<T>>::get(owner.as_sub());2472 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);24732474 <AccountItemCount<T>>::insert(owner.as_sub(), count2475 .checked_add(1)2476 .ok_or(Error::<T>::NumOverflow)?);2477 }2478 else {2479 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2480 }24812482 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2483 if list_exists {2484 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2485 let item_contains = list.contains(&item_index.clone());24862487 if !item_contains {2488 list.push(item_index.clone());2489 }24902491 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2492 } else {2493 let mut itm = Vec::new();2494 itm.push(item_index.clone());2495 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2496 }24972498 Ok(())2499 }25002501 fn remove_token_index(2502 collection_id: CollectionId,2503 item_index: TokenId,2504 owner: &T::CrossAccountId,2505 ) -> DispatchResult {25062507 2508 <AccountItemCount<T>>::insert(owner.as_sub(), 2509 <AccountItemCount<T>>::get(owner.as_sub())2510 .checked_sub(1)2511 .ok_or(Error::<T>::NumOverflow)?);251225132514 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2515 if list_exists {2516 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2517 let item_contains = list.contains(&item_index.clone());25182519 if item_contains {2520 list.retain(|&item| item != item_index);2521 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2522 }2523 }25242525 Ok(())2526 }25272528 fn move_token_index(2529 collection_id: CollectionId,2530 item_index: TokenId,2531 old_owner: &T::CrossAccountId,2532 new_owner: &T::CrossAccountId,2533 ) -> DispatchResult {2534 Self::remove_token_index(collection_id, item_index, old_owner)?;2535 Self::add_token_index(collection_id, item_index, new_owner)?;25362537 Ok(())2538 }2539 2540 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2541 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);25422543 Ok(())2544 }2545}2546254725482549255025512552pub type Multiplier = FixedU128;25532554type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2555255625572558#[derive(Encode, Decode, Clone, Eq, PartialEq)]2559pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);25602561impl<T: Config + Send + Sync> sp_std::fmt::Debug 2562 for ChargeTransactionPayment<T>2563{2564 #[cfg(feature = "std")]2565 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2566 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2567 }2568 #[cfg(not(feature = "std"))]2569 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2570 Ok(())2571 }2572}25732574impl<T: Config> ChargeTransactionPayment<T>2575where2576 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2577 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2578 T::AccountId: AsRef<[u8]>,2579 T::AccountId: UncheckedFrom<T::Hash>,2580{2581 fn traditional_fee(2582 len: usize,2583 info: &DispatchInfoOf<T::Call>,2584 tip: BalanceOf<T>,2585 ) -> BalanceOf<T>2586 where2587 T::Call: Dispatchable<Info = DispatchInfo>,2588 {2589 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2590 }25912592 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2593 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2594 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2595 let len_saturation = max_block_length as u64 / (len as u64).max(1);2596 let coefficient: BalanceOf<T> = weight_saturation2597 .min(len_saturation)2598 .saturated_into::<BalanceOf<T>>();2599 final_fee2600 .saturating_mul(coefficient)2601 .saturated_into::<TransactionPriority>()2602 }26032604 fn withdraw_fee(2605 &self,2606 who: &T::AccountId,2607 call: &T::Call,2608 info: &DispatchInfoOf<T::Call>,2609 len: usize,2610 ) -> Result<2611 (2612 BalanceOf<T>,2613 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2614 ),2615 TransactionValidityError,2616 > {2617 let tip = self.0;26182619 let fee = Self::traditional_fee(len, info, tip);26202621 2622 if fee.is_zero() {2623 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2624 .map(|i| (fee, i));2625 }26262627 2628 2629 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2630 Some(Call::create_item(collection_id, _owner, _properties)) => {2631 let collection = <CollectionById<T>>::get(collection_id)?;26322633 2634 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26352636 let limit = collection.limits.sponsor_transfer_timeout;2637 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2638 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2639 let limit_time = last_tx_block + limit.into();2640 if block_number <= limit_time {2641 return None;2642 }2643 }2644 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);26452646 2647 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2648 collection.sponsorship.sponsor()2649 .cloned()2650 } else {2651 None2652 }2653 }2654 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2655 let collection = <CollectionById<T>>::get(collection_id)?;2656 2657 let mut sponsor_transfer = false;2658 if collection.sponsorship.confirmed() {26592660 let collection_limits = collection.limits;2661 let collection_mode = collection.mode;2662 2663 2664 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2665 sponsor_transfer = match collection_mode {2666 CollectionMode::NFT => {2667 2668 2669 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2670 collection_limits.sponsor_transfer_timeout2671 } else {2672 ChainLimit::get().nft_sponsor_transfer_timeout2673 };2674 2675 let mut sponsored = true;2676 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2677 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2678 let limit_time = last_tx_block + limit.into();2679 if block_number <= limit_time {2680 sponsored = false;2681 }2682 }2683 if sponsored {2684 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2685 }26862687 sponsored2688 }2689 CollectionMode::Fungible(_) => {2690 2691 2692 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2693 collection_limits.sponsor_transfer_timeout2694 } else {2695 ChainLimit::get().fungible_sponsor_transfer_timeout2696 };2697 2698 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2699 let mut sponsored = true;2700 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2701 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2702 let limit_time = last_tx_block + limit.into();2703 if block_number <= limit_time {2704 sponsored = false;2705 }2706 }2707 if sponsored {2708 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2709 }27102711 sponsored2712 }2713 CollectionMode::ReFungible => {2714 2715 2716 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2717 collection_limits.sponsor_transfer_timeout2718 } else {2719 ChainLimit::get().refungible_sponsor_transfer_timeout2720 };2721 2722 let mut sponsored = true;2723 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2724 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2725 let limit_time = last_tx_block + limit.into();2726 if block_number <= limit_time {2727 sponsored = false;2728 }2729 }2730 if sponsored {2731 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2732 }27332734 sponsored2735 }2736 _ => {2737 false2738 },2739 };2740 }27412742 if !sponsor_transfer {2743 None2744 } else {2745 collection.sponsorship.sponsor()2746 .cloned()2747 }2748 }27492750 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2751 let mut sponsor_metadata_changes = false;27522753 let collection = <CollectionById<T>>::get(collection_id)?;27542755 if2756 collection.sponsorship.confirmed() &&2757 2758 2759 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2760 data.len() <= collection.limits.sponsored_data_size as usize2761 {2762 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2763 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27642765 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2766 .map(|last_block| block_number - last_block > rate_limit)2767 .unwrap_or(true) 2768 {2769 sponsor_metadata_changes = true;2770 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2771 }2772 }2773 }27742775 if !sponsor_metadata_changes {2776 None2777 } else {2778 collection.sponsorship.sponsor().cloned()2779 }2780 }27812782 _ => None,2783 })();27842785 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2786 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27872788 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27892790 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2791 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2792 2793 if !owned_contract && white_list_enabled {2794 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2795 return Err(InvalidTransaction::Call.into());2796 }2797 }2798 },2799 _ => {},2800 }28012802 2803 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {28042805 2806 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {28072808 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2809 &who,2810 code_hash,2811 salt,2812 );2813 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28142815 None2816 },28172818 2819 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {28202821 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2822 &who,2823 &T::Hashing::hash(&_code),2824 _salt,2825 );28262827 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28282829 None2830 }28312832 2833 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28342835 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28362837 let mut sponsor_transfer = false;2838 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2839 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2840 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2841 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2842 let limit_time = last_tx_block + rate_limit;28432844 if block_number >= limit_time {2845 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2846 sponsor_transfer = true;2847 }2848 } else {2849 sponsor_transfer = false;2850 }2851 2852 if sponsor_transfer {2853 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2854 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2855 return Some(called_contract);2856 }2857 }2858 }28592860 None2861 },28622863 _ => None,2864 });28652866 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());28672868 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2869 .map(|i| (fee, i))2870 }2871}287228732874impl<T: Config + Send + Sync> SignedExtension2875 for ChargeTransactionPayment<T>2876where2877 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2878 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2879 T::AccountId: AsRef<[u8]>,2880 T::AccountId: UncheckedFrom<T::Hash>,2881{2882 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2883 type AccountId = T::AccountId;2884 type Call = T::Call;2885 type AdditionalSigned = ();2886 type Pre = (2887 2888 BalanceOf<T>,2889 2890 Self::AccountId,2891 2892 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2893 );2894 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2895 Ok(())2896 }28972898 fn validate(2899 &self,2900 who: &Self::AccountId,2901 call: &Self::Call,2902 info: &DispatchInfoOf<Self::Call>,2903 len: usize,2904 ) -> TransactionValidity {2905 let (fee, _) = self.withdraw_fee(who, call, info, len)?;2906 Ok(ValidTransaction {2907 priority: Self::get_priority(len, info, fee),2908 ..Default::default()2909 })2910 }29112912 fn pre_dispatch(2913 self,2914 who: &Self::AccountId,2915 call: &Self::Call,2916 info: &DispatchInfoOf<Self::Call>,2917 len: usize,2918 ) -> Result<Self::Pre, TransactionValidityError> {2919 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2920 Ok((self.0, who.clone(), imbalance))2921 }29222923 fn post_dispatch(2924 pre: Self::Pre,2925 info: &DispatchInfoOf<Self::Call>,2926 post_info: &PostDispatchInfoOf<Self::Call>,2927 len: usize,2928 _result: &DispatchResult,2929 ) -> Result<(), TransactionValidityError> {2930 let (tip, who, imbalance) = pre;2931 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2932 len as u32,2933 info,2934 post_info,2935 tip,2936 );2937 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2938 Ok(())2939 }2940}2941294229432944sp_api::decl_runtime_apis! {2945 pub trait NftApi {2946 2947 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2948 }2949}