123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use core::ops::{Deref, DerefMut};17use codec::{Decode, Encode};18pub use frame_support::{19 construct_runtime, decl_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue,32 transactional,33};3435use frame_system::{self as system, ensure_signed, ensure_root};36use sp_runtime::sp_std::prelude::Vec;37use sp_runtime::{38 traits::{39 Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,40 },41 transaction_validity::{42 TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,43 },44 FixedPointOperand, FixedU128,45};46use sp_runtime::traits::StaticLookup;47use pallet_contracts::chain_extension::UncheckedFrom;48use pallet_transaction_payment::OnChargeTransaction;4950#[cfg(test)]51mod mock;5253#[cfg(test)]54mod tests;5556mod default_weights;5758pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;59pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;60pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;61pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6263646566pub type CollectionId = u32;67pub type TokenId = u32;68pub type DecimalPoints = u8;6970#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]71#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]72pub enum CollectionMode {73 Invalid,74 NFT,75 76 Fungible(DecimalPoints),77 ReFungible,78}7980impl Default for CollectionMode {81 fn default() -> Self {82 Self::Invalid83 }84}8586impl Into<u8> for CollectionMode {87 fn into(self) -> u8 {88 match self {89 CollectionMode::Invalid => 0,90 CollectionMode::NFT => 1,91 CollectionMode::Fungible(_) => 2,92 CollectionMode::ReFungible => 3,93 }94 }95}9697#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]98#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]99pub enum AccessMode {100 Normal,101 WhiteList,102}103impl Default for AccessMode {104 fn default() -> Self {105 Self::Normal106 }107}108109#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]110#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]111pub enum SchemaVersion {112 ImageURL,113 Unique,114}115impl Default for SchemaVersion {116 fn default() -> Self {117 Self::ImageURL118 }119}120121#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]122#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]123pub struct Ownership<AccountId> {124 pub owner: AccountId,125 pub fraction: u128,126}127128#[derive(Encode, Decode, Debug, Clone, PartialEq)]129#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]130pub enum SponsorshipState<AccountId> {131 132 Disabled,133 Unconfirmed(AccountId),134 135 Confirmed(AccountId),136}137138impl<AccountId> SponsorshipState<AccountId> {139 fn sponsor(&self) -> Option<&AccountId> {140 match self {141 Self::Confirmed(sponsor) => Some(sponsor),142 _ => None,143 }144 }145146 fn pending_sponsor(&self) -> Option<&AccountId> {147 match self {148 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),149 _ => None,150 }151 }152153 fn confirmed(&self) -> bool {154 matches!(self, Self::Confirmed(_))155 }156}157158impl<T> Default for SponsorshipState<T> {159 fn default() -> Self {160 Self::Disabled161 }162}163164#[derive(Encode, Decode, Clone, PartialEq)]165#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]166pub struct Collection<T: Config> {167 pub owner: T::AccountId,168 pub mode: CollectionMode,169 pub access: AccessMode,170 pub decimal_points: DecimalPoints,171 pub name: Vec<u16>, 172 pub description: Vec<u16>, 173 pub token_prefix: Vec<u8>, 174 pub mint_mode: bool,175 pub offchain_schema: Vec<u8>,176 pub schema_version: SchemaVersion,177 pub sponsorship: SponsorshipState<T::AccountId>,178 pub limits: CollectionLimits<T::BlockNumber>, 179 pub variable_on_chain_schema: Vec<u8>, 180 pub const_on_chain_schema: Vec<u8>, 181}182183pub struct CollectionHandle<T: Config> {184 pub id: CollectionId,185 collection: Collection<T>,186}187188impl<T: Config> Deref for CollectionHandle<T> {189 type Target = Collection<T>;190191 fn deref(&self) -> &Self::Target {192 &self.collection193 }194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197 fn deref_mut(&mut self) -> &mut Self::Target {198 &mut self.collection199 }200}201202#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]203#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]204pub struct NftItemType<AccountId> {205 pub owner: AccountId,206 pub const_data: Vec<u8>,207 pub variable_data: Vec<u8>,208}209210#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]211#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]212pub struct FungibleItemType {213 pub value: u128,214}215216#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]217#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]218pub struct ReFungibleItemType<AccountId> {219 pub owner: Vec<Ownership<AccountId>>,220 pub const_data: Vec<u8>,221 pub variable_data: Vec<u8>,222}223224225226227228229230231232233234235#[derive(Encode, Decode, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct CollectionLimits<BlockNumber: Encode + Decode> {238 pub account_token_ownership_limit: u32,239 pub sponsored_data_size: u32,240 241 242 243 pub sponsored_data_rate_limit: Option<BlockNumber>,244 pub token_limit: u32,245246 247 pub sponsor_transfer_timeout: u32,248 pub owner_can_transfer: bool,249 pub owner_can_destroy: bool,250}251252impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {253 fn default() -> Self {254 Self { 255 account_token_ownership_limit: 10_000_000, 256 token_limit: u32::max_value(),257 sponsored_data_size: u32::MAX, 258 sponsored_data_rate_limit: None,259 sponsor_transfer_timeout: 14400,260 owner_can_transfer: true,261 owner_can_destroy: true262 }263 }264}265266#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]267#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]268pub struct ChainLimits {269 pub collection_numbers_limit: u32,270 pub account_token_ownership_limit: u32,271 pub collections_admins_limit: u64,272 pub custom_data_limit: u32,273274 275 pub nft_sponsor_transfer_timeout: u32,276 pub fungible_sponsor_transfer_timeout: u32,277 pub refungible_sponsor_transfer_timeout: u32,278279 280 pub offchain_schema_limit: u32,281 pub variable_on_chain_schema_limit: u32,282 pub const_on_chain_schema_limit: u32,283}284285pub trait WeightInfo {286 fn create_collection() -> Weight;287 fn destroy_collection() -> Weight;288 fn add_to_white_list() -> Weight;289 fn remove_from_white_list() -> Weight;290 fn set_public_access_mode() -> Weight;291 fn set_mint_permission() -> Weight;292 fn change_collection_owner() -> Weight;293 fn add_collection_admin() -> Weight;294 fn remove_collection_admin() -> Weight;295 fn set_collection_sponsor() -> Weight;296 fn confirm_sponsorship() -> Weight;297 fn remove_collection_sponsor() -> Weight;298 fn create_item(s: usize) -> Weight;299 fn burn_item() -> Weight;300 fn transfer() -> Weight;301 fn approve() -> Weight;302 fn transfer_from() -> Weight;303 fn set_offchain_schema() -> Weight;304 fn set_const_on_chain_schema() -> Weight;305 fn set_variable_on_chain_schema() -> Weight;306 fn set_variable_meta_data() -> Weight;307 fn enable_contract_sponsoring() -> Weight;308 fn set_schema_version() -> Weight;309 fn set_chain_limits() -> Weight;310 fn set_contract_sponsoring_rate_limit() -> Weight;311 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;312 fn toggle_contract_white_list() -> Weight;313 fn add_to_contract_white_list() -> Weight;314 fn remove_from_contract_white_list() -> Weight;315 fn set_collection_limits() -> Weight;316}317318#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]319#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]320pub struct CreateNftData {321 pub const_data: Vec<u8>,322 pub variable_data: Vec<u8>,323}324325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]326#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]327pub struct CreateFungibleData {328 pub value: u128,329}330331#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]332#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]333pub struct CreateReFungibleData {334 pub const_data: Vec<u8>,335 pub variable_data: Vec<u8>,336 pub pieces: u128,337}338339#[derive(Encode, Decode, Debug, Clone, PartialEq)]340#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]341pub enum CreateItemData {342 NFT(CreateNftData),343 Fungible(CreateFungibleData),344 ReFungible(CreateReFungibleData),345}346347impl CreateItemData {348 pub fn len(&self) -> usize {349 let len = match self {350 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),351 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),352 _ => 0353 };354 355 return len;356 }357}358359impl From<CreateNftData> for CreateItemData {360 fn from(item: CreateNftData) -> Self {361 CreateItemData::NFT(item)362 }363}364365impl From<CreateReFungibleData> for CreateItemData {366 fn from(item: CreateReFungibleData) -> Self {367 CreateItemData::ReFungible(item)368 }369}370371impl From<CreateFungibleData> for CreateItemData {372 fn from(item: CreateFungibleData) -> Self {373 CreateItemData::Fungible(item)374 }375}376377378decl_error! {379 380 pub enum Error for Module<T: Config> {381 382 TotalCollectionsLimitExceeded,383 384 CollectionDecimalPointLimitExceeded, 385 386 CollectionNameLimitExceeded, 387 388 CollectionDescriptionLimitExceeded, 389 390 CollectionTokenPrefixLimitExceeded,391 392 CollectionNotFound,393 394 TokenNotFound,395 396 AdminNotFound,397 398 NumOverflow, 399 400 AlreadyAdmin, 401 402 NoPermission,403 404 ConfirmUnsetSponsorFail,405 406 PublicMintingNotAllowed,407 408 MustBeTokenOwner,409 410 TokenValueTooLow,411 412 NftSizeLimitExceeded,413 414 ApproveNotFound,415 416 TokenValueNotEnough,417 418 ApproveRequired,419 420 AddresNotInWhiteList,421 422 CollectionAdminsLimitExceeded,423 424 AddressOwnershipLimitExceeded,425 426 EmptyArgument,427 428 TokenConstDataLimitExceeded,429 430 TokenVariableDataLimitExceeded,431 432 NotNftDataUsedToMintNftCollectionToken,433 434 NotFungibleDataUsedToMintFungibleCollectionToken,435 436 NotReFungibleDataUsedToMintReFungibleCollectionToken,437 438 UnexpectedCollectionType,439 440 CantStoreMetadataInFungibleTokens,441 442 CollectionTokenLimitExceeded,443 444 AccountTokenLimitExceeded,445 446 CollectionLimitBoundsExceeded,447 448 OwnerPermissionsCantBeReverted,449 450 SchemaDataLimitExceeded,451 452 WrongRefungiblePieces,453 454 BadCreateRefungibleCall,455 }456}457458pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {459 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;460461 462 type WeightInfo: WeightInfo;463464 type Currency: Currency<Self::AccountId>;465 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;466 type TreasuryAccountId: Get<Self::AccountId>;467}468469#[cfg(feature = "runtime-benchmarks")]470mod benchmarking;471472473474475476477478479480481482483484485486487488489490491492493494495496decl_storage! {497 trait Store for Module<T: Config> as Nft {498499 500 501 CreatedCollectionCount: u32;502 503 ChainVersion: u64;504 505 506 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;507 508509 510 pub ChainLimit get(fn chain_limit) config(): ChainLimits;511 512513 514 515 516 DestroyedCollectionCount: u32;517 518 519 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;520 521522 523 524 525 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;526 527 528 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;529 530 531 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;532 533534 535 536 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;537538 539 540 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;541542 543 544 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;545 546 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;547 548 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;549 550551 552 553 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;554 555556 557 558 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;559 560 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;561 562 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;563 564 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;565 566567 568 569 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;570 571 572 573 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;574 575 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;576 577 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;578 579 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;580 581 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 582 583 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 584 585 }586 add_extra_genesis {587 build(|config: &GenesisConfig<T>| {588 589 for (_num, _c) in &config.collection_id {590 <Module<T>>::init_collection(_c);591 }592593 for (_num, _c, _i) in &config.nft_item_id {594 <Module<T>>::init_nft_token(*_c, _i);595 }596597 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {598 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);599 }600601 for (_num, _c, _i) in &config.refungible_item_id {602 <Module<T>>::init_refungible_token(*_c, _i);603 }604 })605 }606}607608decl_event!(609 pub enum Event<T>610 where611 AccountId = <T as system::Config>::AccountId,612 {613 614 615 616 617 618 619 620 621 622 Created(CollectionId, u8, AccountId),623624 625 626 627 628 629 630 631 632 633 ItemCreated(CollectionId, TokenId, AccountId),634635 636 637 638 639 640 641 642 ItemDestroyed(CollectionId, TokenId),643644 645 646 647 648 649 650 651 652 653 654 655 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),656657 658 659 660 661 662 663 664 665 666 Approved(CollectionId, TokenId, AccountId, AccountId, u128),667 }668);669670decl_module! {671 pub struct Module<T: Config> for enum Call 672 where 673 origin: T::Origin674 {675 fn deposit_event() = default;676 type Error = Error<T>;677678 fn on_initialize(now: T::BlockNumber) -> Weight {679 0680 }681682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 #[weight = <T as Config>::WeightInfo::create_collection()]699 #[transactional]700 pub fn create_collection(origin,701 collection_name: Vec<u16>,702 collection_description: Vec<u16>,703 token_prefix: Vec<u8>,704 mode: CollectionMode) -> DispatchResult {705706 707 let who = ensure_signed(origin)?;708709 710 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();711 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(712 &T::TreasuryAccountId::get(),713 T::CollectionCreationPrice::get(),714 ));715 <T as Config>::Currency::settle(716 &who,717 imbalance,718 WithdrawReasons::TRANSFER,719 ExistenceRequirement::KeepAlive,720 ).map_err(|_| Error::<T>::NoPermission)?;721722 let decimal_points = match mode {723 CollectionMode::Fungible(points) => points,724 _ => 0725 };726727 let chain_limit = ChainLimit::get();728729 let created_count = CreatedCollectionCount::get();730 let destroyed_count = DestroyedCollectionCount::get();731732 733 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);734735 736 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);737 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);738 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);739 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);740741 742 let next_id = created_count743 .checked_add(1)744 .ok_or(Error::<T>::NumOverflow)?;745746 CreatedCollectionCount::put(next_id);747748 let limits = CollectionLimits {749 sponsored_data_size: chain_limit.custom_data_limit,750 ..Default::default()751 };752753 754 let new_collection = Collection {755 owner: who.clone(),756 name: collection_name,757 mode: mode.clone(),758 mint_mode: false,759 access: AccessMode::Normal,760 description: collection_description,761 decimal_points: decimal_points,762 token_prefix: token_prefix,763 offchain_schema: Vec::new(),764 schema_version: SchemaVersion::ImageURL,765 sponsorship: SponsorshipState::Disabled,766 variable_on_chain_schema: Vec::new(),767 const_on_chain_schema: Vec::new(),768 limits,769 };770771 772 <CollectionById<T>>::insert(next_id, new_collection);773774 775 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));776777 Ok(())778 }779780 781 782 783 784 785 786 787 788 789 #[weight = <T as Config>::WeightInfo::destroy_collection()]790 #[transactional]791 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {792793 let sender = ensure_signed(origin)?;794 let collection = Self::get_collection(collection_id)?;795 Self::check_owner_permissions(&collection, sender)?;796 if !collection.limits.owner_can_destroy {797 fail!(Error::<T>::NoPermission);798 }799800 <AddressTokens<T>>::remove_prefix(collection_id);801 <Allowances<T>>::remove_prefix(collection_id);802 <Balance<T>>::remove_prefix(collection_id);803 <ItemListIndex>::remove(collection_id);804 <AdminList<T>>::remove(collection_id);805 <CollectionById<T>>::remove(collection_id);806 <WhiteList<T>>::remove_prefix(collection_id);807808 <NftItemList<T>>::remove_prefix(collection_id);809 <FungibleItemList<T>>::remove_prefix(collection_id);810 <ReFungibleItemList<T>>::remove_prefix(collection_id);811812 <NftTransferBasket<T>>::remove_prefix(collection_id);813 <FungibleTransferBasket<T>>::remove_prefix(collection_id);814 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);815816 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);817818 DestroyedCollectionCount::put(DestroyedCollectionCount::get()819 .checked_add(1)820 .ok_or(Error::<T>::NumOverflow)?);821822 Ok(())823 }824825 826 827 828 829 830 831 832 833 834 835 836 837 #[weight = <T as Config>::WeightInfo::add_to_white_list()]838 #[transactional]839 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{840841 let sender = ensure_signed(origin)?;842 let collection = Self::get_collection(collection_id)?;843 Self::check_owner_or_admin_permissions(&collection, sender)?;844845 <WhiteList<T>>::insert(collection_id, address, true);846 847 Ok(())848 }849850 851 852 853 854 855 856 857 858 859 860 861 862 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]863 #[transactional]864 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{865866 let sender = ensure_signed(origin)?;867 let collection = Self::get_collection(collection_id)?;868 Self::check_owner_or_admin_permissions(&collection, sender)?;869870 <WhiteList<T>>::remove(collection_id, address);871872 Ok(())873 }874875 876 877 878 879 880 881 882 883 884 885 886 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]887 #[transactional]888 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult889 {890 let sender = ensure_signed(origin)?;891892 let mut target_collection = Self::get_collection(collection_id)?;893 Self::check_owner_permissions(&target_collection, sender)?;894 target_collection.access = mode;895 Self::save_collection(target_collection);896897 Ok(())898 }899900 901 902 903 904 905 906 907 908 909 910 911 912 913 #[weight = <T as Config>::WeightInfo::set_mint_permission()]914 #[transactional]915 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult916 {917 let sender = ensure_signed(origin)?;918919 let mut target_collection = Self::get_collection(collection_id)?;920 Self::check_owner_permissions(&target_collection, sender)?;921 target_collection.mint_mode = mint_permission;922 Self::save_collection(target_collection);923924 Ok(())925 }926927 928 929 930 931 932 933 934 935 936 937 938 #[weight = <T as Config>::WeightInfo::change_collection_owner()]939 #[transactional]940 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {941942 let sender = ensure_signed(origin)?;943 let mut target_collection = Self::get_collection(collection_id)?;944 Self::check_owner_permissions(&target_collection, sender)?;945 target_collection.owner = new_owner;946 Self::save_collection(target_collection);947948 Ok(())949 }950951 952 953 954 955 956 957 958 959 960 961 962 963 964 #[weight = <T as Config>::WeightInfo::add_collection_admin()]965 #[transactional]966 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {967968 let sender = ensure_signed(origin)?;969 let collection = Self::get_collection(collection_id)?;970 Self::check_owner_or_admin_permissions(&collection, sender)?;971 let mut admin_arr: Vec<T::AccountId> = Vec::new();972973 if <AdminList<T>>::contains_key(collection_id)974 {975 admin_arr = <AdminList<T>>::get(collection_id);976 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);977 }978979 980 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);981982 admin_arr.push(new_admin_id);983 <AdminList<T>>::insert(collection_id, admin_arr);984985 Ok(())986 }987988 989 990 991 992 993 994 995 996 997 998 999 1000 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1001 #[transactional]1002 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {10031004 let sender = ensure_signed(origin)?;1005 let collection = Self::get_collection(collection_id)?;1006 Self::check_owner_or_admin_permissions(&collection, sender)?;1007 ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);10081009 let mut admin_arr = <AdminList<T>>::get(collection_id);1010 admin_arr.retain(|i| *i != account_id);1011 <AdminList<T>>::insert(collection_id, admin_arr);10121013 Ok(())1014 }10151016 1017 1018 1019 1020 1021 1022 1023 1024 1025 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1026 #[transactional]1027 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {10281029 let sender = ensure_signed(origin)?;1030 let mut target_collection = Self::get_collection(collection_id)?;1031 Self::check_owner_permissions(&target_collection, sender)?;10321033 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1034 Self::save_collection(target_collection);10351036 Ok(())1037 }10381039 1040 1041 1042 1043 1044 1045 1046 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1047 #[transactional]1048 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {10491050 let sender = ensure_signed(origin)?;10511052 let mut target_collection = Self::get_collection(collection_id)?;1053 ensure!(1054 target_collection.sponsorship.pending_sponsor() == Some(&sender),1055 Error::<T>::ConfirmUnsetSponsorFail1056 );10571058 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1059 Self::save_collection(target_collection);10601061 Ok(())1062 }10631064 1065 1066 1067 1068 1069 1070 1071 1072 1073 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1074 #[transactional]1075 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10761077 let sender = ensure_signed(origin)?;10781079 let mut target_collection = Self::get_collection(collection_id)?;1080 Self::check_owner_permissions(&target_collection, sender)?;10811082 target_collection.sponsorship = SponsorshipState::Disabled;1083 Self::save_collection(target_collection);10841085 Ok(())1086 }10871088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 11111112 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1113 #[transactional]1114 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {11151116 let sender = ensure_signed(origin)?;11171118 let target_collection = Self::get_collection(collection_id)?;11191120 Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1121 Self::validate_create_item_args(&target_collection, &data)?;1122 Self::create_item_no_validation(&target_collection, owner, data)?;11231124 Ok(())1125 }11261127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1146 .map(|data| { data.len() })1147 .sum())]1148 #[transactional]1149 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11501151 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1152 let sender = ensure_signed(origin)?;11531154 let target_collection = Self::get_collection(collection_id)?;11551156 Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;11571158 for data in &items_data {1159 Self::validate_create_item_args(&target_collection, data)?;1160 }1161 for data in &items_data {1162 Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1163 }11641165 Ok(())1166 }11671168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 #[weight = <T as Config>::WeightInfo::burn_item()]1182 #[transactional]1183 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11841185 let sender = ensure_signed(origin)?;11861187 1188 let target_collection = Self::get_collection(collection_id)?;1189 ensure!(1190 Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1191 (1192 target_collection.limits.owner_can_transfer &&1193 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1194 ),1195 Error::<T>::NoPermission1196 );11971198 if target_collection.access == AccessMode::WhiteList {1199 Self::check_white_list(&target_collection, &sender)?;1200 }12011202 match target_collection.mode1203 {1204 CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1205 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?,1206 CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1207 _ => ()1208 };12091210 1211 Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));12121213 Ok(())1214 }12151216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 #[weight = <T as Config>::WeightInfo::transfer()]1240 #[transactional]1241 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1242 let sender = ensure_signed(origin)?;1243 let collection = Self::get_collection(collection_id)?;12441245 Self::transfer_internal(sender, recipient, &collection, item_id, value)1246 }12471248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 #[weight = <T as Config>::WeightInfo::approve()]1264 #[transactional]1265 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12661267 let sender = ensure_signed(origin)?;1268 let target_collection = Self::get_collection(collection_id)?;12691270 Self::token_exists(&target_collection, item_id, &sender)?;12711272 1273 let bypasses_limits = target_collection.limits.owner_can_transfer &&1274 Self::is_owner_or_admin_permissions(1275 &target_collection,1276 sender.clone(),1277 );12781279 let allowance_limit = if bypasses_limits {1280 None1281 } else if let Some(amount) = Self::owned_amount(1282 sender.clone(),1283 &target_collection,1284 item_id,1285 ) {1286 Some(amount)1287 } else {1288 fail!(Error::<T>::NoPermission);1289 };12901291 if target_collection.access == AccessMode::WhiteList {1292 Self::check_white_list(&target_collection, &sender)?;1293 Self::check_white_list(&target_collection, &spender)?;1294 }12951296 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1297 let mut allowance: u128 = amount;1298 if allowance_exists {1299 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1300 }1301 if let Some(limit) = allowance_limit {1302 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1303 }1304 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);13051306 Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1307 Ok(())1308 }1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 #[weight = <T as Config>::WeightInfo::transfer_from()]1330 #[transactional]1331 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {13321333 let sender = ensure_signed(origin)?;1334 let target_collection = Self::get_collection(collection_id)?;13351336 let mut appoved_transfer = false;13371338 1339 let mut approval: u128 = 0;1340 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1341 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1342 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1343 appoved_transfer = true;1344 }13451346 1347 Self::is_correct_transfer(&target_collection, &recipient)?;13481349 1350 ensure!(1351 appoved_transfer || 1352 (1353 target_collection.limits.owner_can_transfer &&1354 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1355 ),1356 Error::<T>::NoPermission1357 );13581359 if target_collection.access == AccessMode::WhiteList {1360 Self::check_white_list(&target_collection, &sender)?;1361 Self::check_white_list(&target_collection, &recipient)?;1362 }13631364 1365 if approval.saturating_sub(value) > 0 {1366 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1367 }1368 else {1369 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1370 }13711372 match target_collection.mode1373 {1374 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1375 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1376 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1377 _ => ()1378 };13791380 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1381 Ok(())1382 }13831384 1385 13861387 1388 1389 1390 13911392 13931394 13951396 1397 13981399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1412 #[transactional]1413 pub fn set_variable_meta_data (1414 origin,1415 collection_id: CollectionId,1416 item_id: TokenId,1417 data: Vec<u8>1418 ) -> DispatchResult {1419 let sender = ensure_signed(origin)?;1420 1421 let target_collection = Self::get_collection(collection_id)?;1422 Self::token_exists(&target_collection, item_id, &sender)?;14231424 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);14251426 1427 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1428 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1429 Error::<T>::NoPermission);14301431 match target_collection.mode1432 {1433 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1434 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1435 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1436 _ => fail!(Error::<T>::UnexpectedCollectionType)1437 };14381439 Ok(())1440 }1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 #[weight = <T as Config>::WeightInfo::set_schema_version()]1457 #[transactional]1458 pub fn set_schema_version(1459 origin,1460 collection_id: CollectionId,1461 version: SchemaVersion1462 ) -> DispatchResult {1463 let sender = ensure_signed(origin)?;1464 let mut target_collection = Self::get_collection(collection_id)?;1465 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1466 target_collection.schema_version = version;1467 Self::save_collection(target_collection);14681469 Ok(())1470 }14711472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1485 #[transactional]1486 pub fn set_offchain_schema(1487 origin,1488 collection_id: CollectionId,1489 schema: Vec<u8>1490 ) -> DispatchResult {1491 let sender = ensure_signed(origin)?;1492 let mut target_collection = Self::get_collection(collection_id)?;1493 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14941495 1496 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14971498 target_collection.offchain_schema = schema;1499 Self::save_collection(target_collection);15001501 Ok(())1502 }15031504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1517 #[transactional]1518 pub fn set_const_on_chain_schema (1519 origin,1520 collection_id: CollectionId,1521 schema: Vec<u8>1522 ) -> DispatchResult {1523 let sender = ensure_signed(origin)?;1524 let mut target_collection = Self::get_collection(collection_id)?;1525 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;15261527 1528 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");15291530 target_collection.const_on_chain_schema = schema;1531 Self::save_collection(target_collection);15321533 Ok(())1534 }15351536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1549 #[transactional]1550 pub fn set_variable_on_chain_schema (1551 origin,1552 collection_id: CollectionId,1553 schema: Vec<u8>1554 ) -> DispatchResult {1555 let sender = ensure_signed(origin)?;1556 let mut target_collection = Self::get_collection(collection_id)?;1557 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;15581559 1560 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");15611562 target_collection.variable_on_chain_schema = schema;1563 Self::save_collection(target_collection);15641565 Ok(())1566 }15671568 1569 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1570 #[transactional]1571 pub fn set_chain_limits(1572 origin,1573 limits: ChainLimits1574 ) -> DispatchResult {15751576 #[cfg(not(feature = "runtime-benchmarks"))]1577 ensure_root(origin)?;15781579 <ChainLimit>::put(limits);1580 Ok(())1581 }15821583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1595 #[transactional]1596 pub fn enable_contract_sponsoring(1597 origin,1598 contract_address: T::AccountId,1599 enable: bool1600 ) -> DispatchResult {16011602 let sender = ensure_signed(origin)?;16031604 #[cfg(feature = "runtime-benchmarks")]1605 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16061607 Self::ensure_contract_owned(sender, &contract_address)?;16081609 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1610 Ok(())1611 }16121613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1631 #[transactional]1632 pub fn set_contract_sponsoring_rate_limit(1633 origin,1634 contract_address: T::AccountId,1635 rate_limit: T::BlockNumber1636 ) -> DispatchResult {1637 let sender = ensure_signed(origin)?;16381639 #[cfg(feature = "runtime-benchmarks")]1640 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16411642 Self::ensure_contract_owned(sender, &contract_address)?;1643 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1644 Ok(())1645 }16461647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1659 #[transactional]1660 pub fn toggle_contract_white_list(1661 origin,1662 contract_address: T::AccountId,1663 enable: bool1664 ) -> DispatchResult {1665 let sender = ensure_signed(origin)?;16661667 #[cfg(feature = "runtime-benchmarks")]1668 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16691670 Self::ensure_contract_owned(sender, &contract_address)?;1671 <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1672 Ok(())1673 }1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1687 #[transactional]1688 pub fn add_to_contract_white_list(1689 origin,1690 contract_address: T::AccountId,1691 account_address: T::AccountId1692 ) -> DispatchResult {1693 let sender = ensure_signed(origin)?;16941695 #[cfg(feature = "runtime-benchmarks")]1696 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1697 1698 Self::ensure_contract_owned(sender, &contract_address)?; 1699 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1700 Ok(())1701 }17021703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1715 #[transactional]1716 pub fn remove_from_contract_white_list(1717 origin,1718 contract_address: T::AccountId,1719 account_address: T::AccountId1720 ) -> DispatchResult {1721 let sender = ensure_signed(origin)?;17221723 #[cfg(feature = "runtime-benchmarks")]1724 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());17251726 Self::ensure_contract_owned(sender, &contract_address)?;1727 <ContractWhiteList<T>>::remove(contract_address, account_address);1728 Ok(())1729 }17301731 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1732 #[transactional]1733 pub fn set_collection_limits(1734 origin,1735 collection_id: u32,1736 new_limits: CollectionLimits<T::BlockNumber>,1737 ) -> DispatchResult {1738 let sender = ensure_signed(origin)?;1739 let mut target_collection = Self::get_collection(collection_id)?;1740 Self::check_owner_permissions(&target_collection, sender.clone())?;1741 let old_limits = &target_collection.limits;1742 let chain_limits = ChainLimit::get();17431744 1745 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1746 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1747 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1748 Error::<T>::CollectionLimitBoundsExceeded);17491750 1751 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1752 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);17531754 ensure!(1755 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1756 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1757 Error::<T>::OwnerPermissionsCantBeReverted,1758 );17591760 target_collection.limits = new_limits;1761 Self::save_collection(target_collection);17621763 Ok(())1764 } 1765 }1766}17671768impl<T: Config> Module<T> {17691770 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1771 1772 Self::is_correct_transfer(target_collection, &recipient)?;17731774 1775 ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1776 Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1777 Error::<T>::NoPermission);17781779 if target_collection.access == AccessMode::WhiteList {1780 Self::check_white_list(target_collection, &sender)?;1781 Self::check_white_list(target_collection, &recipient)?;1782 }17831784 match target_collection.mode1785 {1786 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1787 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1788 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1789 _ => ()1790 };17911792 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17931794 Ok(())1795 }179617971798 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1799 let collection_id = collection.id;18001801 1802 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1803 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1804 1805 Ok(())1806 }18071808 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1809 let collection_id = collection.id;18101811 1812 let total_items: u32 = ItemListIndex::get(collection_id)1813 .checked_add(amount)1814 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1815 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1816 .checked_add(amount)1817 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1818 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1819 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);18201821 if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1822 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1823 Self::check_white_list(collection, owner)?;1824 Self::check_white_list(collection, sender)?;1825 }18261827 Ok(())1828 }18291830 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1831 match target_collection.mode1832 {1833 CollectionMode::NFT => {1834 if let CreateItemData::NFT(data) = data {1835 1836 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1837 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1838 } else {1839 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1840 }1841 },1842 CollectionMode::Fungible(_) => {1843 if let CreateItemData::Fungible(_) = data {1844 } else {1845 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1846 }1847 },1848 CollectionMode::ReFungible => {1849 if let CreateItemData::ReFungible(data) = data {18501851 1852 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1853 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18541855 1856 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1857 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1858 } else {1859 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1860 }1861 },1862 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1863 };18641865 Ok(())1866 }18671868 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1869 match data1870 {1871 CreateItemData::NFT(data) => {1872 let item = NftItemType {1873 owner: owner.clone(),1874 const_data: data.const_data,1875 variable_data: data.variable_data1876 };18771878 Self::add_nft_item(collection, item)?;1879 },1880 CreateItemData::Fungible(data) => {1881 Self::add_fungible_item(collection, &owner, data.value)?;1882 },1883 CreateItemData::ReFungible(data) => {1884 let mut owner_list = Vec::new();1885 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});18861887 let item = ReFungibleItemType {1888 owner: owner_list,1889 const_data: data.const_data,1890 variable_data: data.variable_data1891 };18921893 Self::add_refungible_item(collection, item)?;1894 }1895 };18961897 Ok(())1898 }18991900 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1901 let collection_id = collection.id;19021903 1904 let mut balance: u128 = 0;1905 if <FungibleItemList<T>>::contains_key(collection_id, owner) {1906 balance = <FungibleItemList<T>>::get(collection_id, owner).value;1907 } 19081909 1910 let item = FungibleItemType {1911 value: balance + value1912 };1913 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);19141915 1916 let new_balance = <Balance<T>>::get(collection_id, owner)1917 .checked_add(value)1918 .ok_or(Error::<T>::NumOverflow)?;1919 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19201921 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1922 Ok(())1923 }19241925 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1926 let collection_id = collection.id;19271928 let current_index = <ItemListIndex>::get(collection_id)1929 .checked_add(1)1930 .ok_or(Error::<T>::NumOverflow)?;1931 let itemcopy = item.clone();19321933 ensure!(1934 item.owner.len() == 1,1935 Error::<T>::BadCreateRefungibleCall,1936 );1937 let item_owner = item.owner.first().expect("only one owner is defined");19381939 let value = item_owner.fraction;1940 let owner = item_owner.owner.clone();19411942 Self::add_token_index(collection_id, current_index, &owner)?;19431944 <ItemListIndex>::insert(collection_id, current_index);1945 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);19461947 1948 let new_balance = <Balance<T>>::get(collection_id, &owner)1949 .checked_add(value)1950 .ok_or(Error::<T>::NumOverflow)?;1951 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);19521953 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1954 Ok(())1955 }19561957 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1958 let collection_id = collection.id;19591960 let current_index = <ItemListIndex>::get(collection_id)1961 .checked_add(1)1962 .ok_or(Error::<T>::NumOverflow)?;19631964 let item_owner = item.owner.clone();1965 Self::add_token_index(collection_id, current_index, &item.owner)?;19661967 <ItemListIndex>::insert(collection_id, current_index);1968 <NftItemList<T>>::insert(collection_id, current_index, item);19691970 1971 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1972 .checked_add(1)1973 .ok_or(Error::<T>::NumOverflow)?;1974 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);19751976 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1977 Ok(())1978 }19791980 fn burn_refungible_item(1981 collection: &CollectionHandle<T>,1982 item_id: TokenId,1983 owner: &T::AccountId,1984 ) -> DispatchResult {1985 let collection_id = collection.id;19861987 ensure!(1988 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1989 Error::<T>::TokenNotFound1990 );1991 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1992 let rft_balance = token1993 .owner1994 .iter()1995 .find(|&i| i.owner == *owner)1996 .ok_or(Error::<T>::TokenNotFound)?;1997 Self::remove_token_index(collection_id, item_id, owner)?;19981999 2000 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())2001 .checked_sub(rft_balance.fraction)2002 .ok_or(Error::<T>::NumOverflow)?;2003 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);20042005 2006 let index = token2007 .owner2008 .iter()2009 .position(|i| i.owner == *owner)2010 .expect("owned item is exists");2011 token.owner.remove(index);2012 let owner_count = token.owner.len();20132014 2015 if owner_count == 0 {2016 <ReFungibleItemList<T>>::remove(collection_id, item_id);2017 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2018 }2019 else {2020 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2021 }20222023 Ok(())2024 }20252026 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2027 let collection_id = collection.id;20282029 ensure!(2030 <NftItemList<T>>::contains_key(collection_id, item_id),2031 Error::<T>::TokenNotFound2032 );2033 let item = <NftItemList<T>>::get(collection_id, item_id);2034 Self::remove_token_index(collection_id, item_id, &item.owner)?;20352036 2037 let new_balance = <Balance<T>>::get(collection_id, &item.owner)2038 .checked_sub(1)2039 .ok_or(Error::<T>::NumOverflow)?;2040 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);2041 <NftItemList<T>>::remove(collection_id, item_id);2042 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);20432044 Ok(())2045 }20462047 fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2048 let collection_id = collection.id;20492050 ensure!(2051 <FungibleItemList<T>>::contains_key(collection_id, owner),2052 Error::<T>::TokenNotFound2053 );2054 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2055 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);20562057 2058 let new_balance = <Balance<T>>::get(collection_id, owner)2059 .checked_sub(value)2060 .ok_or(Error::<T>::NumOverflow)?;2061 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);20622063 if balance.value - value > 0 {2064 balance.value -= value;2065 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2066 }2067 else {2068 <FungibleItemList<T>>::remove(collection_id, owner);2069 }20702071 Ok(())2072 }20732074 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2075 Ok(<CollectionById<T>>::get(collection_id)2076 .map(|collection| CollectionHandle {2077 id: collection_id,2078 collection2079 })2080 .ok_or(Error::<T>::CollectionNotFound)?)2081 }20822083 fn save_collection(collection: CollectionHandle<T>) {2084 <CollectionById<T>>::insert(collection.id, collection.collection);2085 }20862087 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {2088 ensure!(2089 subject == target_collection.owner,2090 Error::<T>::NoPermission2091 );20922093 Ok(())2094 }20952096 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {2097 let mut result: bool = subject == collection.owner;2098 let exists = <AdminList<T>>::contains_key(collection.id);20992100 if !result & exists {2101 if <AdminList<T>>::get(collection.id).contains(&subject) {2102 result = true2103 }2104 }21052106 result2107 }21082109 fn check_owner_or_admin_permissions(2110 collection: &CollectionHandle<T>,2111 subject: T::AccountId,2112 ) -> DispatchResult {2113 let result = Self::is_owner_or_admin_permissions(collection, subject.clone());21142115 ensure!(2116 result,2117 Error::<T>::NoPermission2118 );2119 Ok(())2120 }21212122 fn owned_amount(2123 subject: T::AccountId,2124 target_collection: &CollectionHandle<T>,2125 item_id: TokenId,2126 ) -> Option<u128> {2127 let collection_id = target_collection.id;21282129 match target_collection.mode {2130 CollectionMode::NFT => {2131 if <NftItemList<T>>::get(collection_id, item_id).owner == subject {2132 return Some(1)2133 }2134 None2135 },2136 CollectionMode::Fungible(_) => {2137 if <FungibleItemList<T>>::contains_key(collection_id, &subject) {2138 return Some(<FungibleItemList<T>>::get(collection_id, &subject)2139 .value);2140 }2141 None2142 },2143 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)2144 .owner2145 .iter()2146 .find(|i| i.owner == subject)2147 .map(|i| i.fraction),2148 CollectionMode::Invalid => None,2149 }2150 }21512152 fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2153 let collection_id = target_collection.id;21542155 match target_collection.mode {2156 CollectionMode::NFT => {2157 <NftItemList<T>>::get(collection_id, item_id).owner == subject2158 }2159 CollectionMode::Fungible(_) => {2160 <FungibleItemList<T>>::contains_key(collection_id, &subject)2161 }2162 CollectionMode::ReFungible => {2163 <ReFungibleItemList<T>>::get(collection_id, item_id)2164 .owner2165 .iter()2166 .any(|i| i.owner == subject)2167 }2168 CollectionMode::Invalid => false,2169 }2170 }21712172 fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {2173 let collection_id = collection.id;21742175 let mes = Error::<T>::AddresNotInWhiteList;2176 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);21772178 Ok(())2179 }21802181 2182 2183 fn token_exists(2184 target_collection: &CollectionHandle<T>,2185 item_id: TokenId,2186 owner: &T::AccountId2187 ) -> 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(_) => <FungibleItemList<T>>::contains_key(collection_id, owner),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::AccountId,2205 recipient: &T::AccountId,2206 ) -> DispatchResult {2207 let collection_id = collection.id;2208 Self::token_exists(&collection, 0, owner)?;22092210 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2211 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);22122213 2214 Self::add_fungible_item(collection, recipient, value)?;22152216 2217 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);22182219 2220 if balance.value == value {2221 <FungibleItemList<T>>::remove(collection_id, owner);2222 }2223 else {2224 balance.value -= value;2225 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2226 }22272228 Ok(())2229 }22302231 fn transfer_refungible(2232 collection: &CollectionHandle<T>,2233 item_id: TokenId,2234 value: u128,2235 owner: T::AccountId,2236 new_owner: T::AccountId,2237 ) -> DispatchResult {2238 let collection_id = collection.id;2239 Self::token_exists(collection, item_id, &owner)?;22402241 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2242 let item = full_item2243 .owner2244 .iter()2245 .filter(|i| i.owner == owner)2246 .next()2247 .ok_or(Error::<T>::TokenNotFound)?;2248 let amount = item.fraction;22492250 ensure!(amount >= value, Error::<T>::TokenValueTooLow);22512252 2253 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2254 .checked_sub(value)2255 .ok_or(Error::<T>::NumOverflow)?;2256 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22572258 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2259 .checked_add(value)2260 .ok_or(Error::<T>::NumOverflow)?;2261 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);22622263 let old_owner = item.owner.clone();2264 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);22652266 2267 if amount == value && !new_owner_has_account {2268 2269 2270 let mut new_full_item = full_item.clone();2271 new_full_item2272 .owner2273 .iter_mut()2274 .find(|i| i.owner == owner)2275 .expect("old owner does present in refungible")2276 .owner = new_owner.clone();2277 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);22782279 2280 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2281 } else {2282 let mut new_full_item = full_item.clone();2283 new_full_item2284 .owner2285 .iter_mut()2286 .find(|i| i.owner == owner)2287 .expect("old owner does present in refungible")2288 .fraction -= value;22892290 2291 if new_owner_has_account {2292 2293 new_full_item2294 .owner2295 .iter_mut()2296 .find(|i| i.owner == new_owner)2297 .expect("new owner has account")2298 .fraction += value;2299 } else {2300 2301 new_full_item.owner.push(Ownership {2302 owner: new_owner.clone(),2303 fraction: value,2304 });2305 Self::add_token_index(collection_id, item_id, &new_owner)?;2306 }23072308 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2309 }23102311 Ok(())2312 }23132314 fn transfer_nft(2315 collection: &CollectionHandle<T>,2316 item_id: TokenId,2317 sender: T::AccountId,2318 new_owner: T::AccountId,2319 ) -> DispatchResult {2320 let collection_id = collection.id;2321 Self::token_exists(&collection, item_id, &sender)?;23222323 let mut item = <NftItemList<T>>::get(collection_id, item_id);23242325 ensure!(2326 sender == item.owner,2327 Error::<T>::MustBeTokenOwner2328 );23292330 2331 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2332 .checked_sub(1)2333 .ok_or(Error::<T>::NumOverflow)?;2334 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);23352336 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2337 .checked_add(1)2338 .ok_or(Error::<T>::NumOverflow)?;2339 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);23402341 2342 let old_owner = item.owner.clone();2343 item.owner = new_owner.clone();2344 <NftItemList<T>>::insert(collection_id, item_id, item);23452346 2347 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;23482349 Ok(())2350 }2351 2352 fn set_re_fungible_variable_data(2353 collection: &CollectionHandle<T>,2354 item_id: TokenId,2355 data: Vec<u8>2356 ) -> DispatchResult {2357 let collection_id = collection.id;2358 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);23592360 item.variable_data = data;23612362 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);23632364 Ok(())2365 }23662367 fn set_nft_variable_data(2368 collection: &CollectionHandle<T>,2369 item_id: TokenId,2370 data: Vec<u8>2371 ) -> DispatchResult {2372 let collection_id = collection.id;2373 let mut item = <NftItemList<T>>::get(collection_id, item_id);2374 2375 item.variable_data = data;23762377 <NftItemList<T>>::insert(collection_id, item_id, item);2378 2379 Ok(())2380 }23812382 fn init_collection(item: &Collection<T>) {2383 2384 assert!(2385 item.decimal_points <= MAX_DECIMAL_POINTS,2386 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2387 );2388 assert!(2389 item.name.len() <= 64,2390 "Collection name can not be longer than 63 char"2391 );2392 assert!(2393 item.name.len() <= 256,2394 "Collection description can not be longer than 255 char"2395 );2396 assert!(2397 item.token_prefix.len() <= 16,2398 "Token prefix can not be longer than 15 char"2399 );24002401 2402 let next_id = CreatedCollectionCount::get()2403 .checked_add(1)2404 .unwrap();24052406 CreatedCollectionCount::put(next_id);2407 }24082409 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2410 let current_index = <ItemListIndex>::get(collection_id)2411 .checked_add(1)2412 .unwrap();24132414 let item_owner = item.owner.clone();2415 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();24162417 <ItemListIndex>::insert(collection_id, current_index);24182419 2420 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2421 .checked_add(1)2422 .unwrap();2423 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2424 }24252426 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2427 let current_index = <ItemListIndex>::get(collection_id)2428 .checked_add(1)2429 .unwrap();24302431 Self::add_token_index(collection_id, current_index, owner).unwrap();24322433 <ItemListIndex>::insert(collection_id, current_index);24342435 2436 let new_balance = <Balance<T>>::get(collection_id, owner)2437 .checked_add(item.value)2438 .unwrap();2439 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2440 }24412442 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2443 let current_index = <ItemListIndex>::get(collection_id)2444 .checked_add(1)2445 .unwrap();24462447 let value = item.owner.first().unwrap().fraction;2448 let owner = item.owner.first().unwrap().owner.clone();24492450 Self::add_token_index(collection_id, current_index, &owner).unwrap();24512452 <ItemListIndex>::insert(collection_id, current_index);24532454 2455 let new_balance = <Balance<T>>::get(collection_id, &owner)2456 .checked_add(value)2457 .unwrap();2458 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2459 }24602461 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2462 2463 if <AccountItemCount<T>>::contains_key(owner) {24642465 2466 let count = <AccountItemCount<T>>::get(owner);2467 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);24682469 <AccountItemCount<T>>::insert(owner.clone(), count2470 .checked_add(1)2471 .ok_or(Error::<T>::NumOverflow)?);2472 }2473 else {2474 <AccountItemCount<T>>::insert(owner.clone(), 1);2475 }24762477 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2478 if list_exists {2479 let mut list = <AddressTokens<T>>::get(collection_id, owner);2480 let item_contains = list.contains(&item_index.clone());24812482 if !item_contains {2483 list.push(item_index.clone());2484 }24852486 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2487 } else {2488 let mut itm = Vec::new();2489 itm.push(item_index.clone());2490 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2491 }24922493 Ok(())2494 }24952496 fn remove_token_index(2497 collection_id: CollectionId,2498 item_index: TokenId,2499 owner: &T::AccountId,2500 ) -> DispatchResult {25012502 2503 <AccountItemCount<T>>::insert(owner.clone(), 2504 <AccountItemCount<T>>::get(owner)2505 .checked_sub(1)2506 .ok_or(Error::<T>::NumOverflow)?);250725082509 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2510 if list_exists {2511 let mut list = <AddressTokens<T>>::get(collection_id, owner);2512 let item_contains = list.contains(&item_index.clone());25132514 if item_contains {2515 list.retain(|&item| item != item_index);2516 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2517 }2518 }25192520 Ok(())2521 }25222523 fn move_token_index(2524 collection_id: CollectionId,2525 item_index: TokenId,2526 old_owner: &T::AccountId,2527 new_owner: &T::AccountId,2528 ) -> DispatchResult {2529 Self::remove_token_index(collection_id, item_index, old_owner)?;2530 Self::add_token_index(collection_id, item_index, new_owner)?;25312532 Ok(())2533 }2534 2535 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2536 if <ContractOwner<T>>::contains_key(contract.clone()) {2537 let owner = <ContractOwner<T>>::get(contract);2538 ensure!(account == owner, Error::<T>::NoPermission);2539 } else {2540 fail!(Error::<T>::NoPermission);2541 }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>>::contains_key(called_contract.clone())2791 && <ContractOwner<T>>::get(called_contract.clone()) == *who;2792 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2793 2794 if !owned_contract && white_list_enabled {2795 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2796 return Err(InvalidTransaction::Call.into());2797 }2798 }2799 },2800 _ => {},2801 }28022803 2804 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {28052806 2807 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {28082809 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2810 &who,2811 code_hash,2812 salt,2813 );2814 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28152816 None2817 },28182819 2820 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {28212822 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2823 &who,2824 &T::Hashing::hash(&_code),2825 _salt,2826 );28272828 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28292830 None2831 }28322833 2834 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28352836 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28372838 let mut sponsor_transfer = false;2839 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2840 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2841 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2842 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2843 let limit_time = last_tx_block + rate_limit;28442845 if block_number >= limit_time {2846 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2847 sponsor_transfer = true;2848 }2849 } else {2850 sponsor_transfer = false;2851 }2852 2853 if sponsor_transfer {2854 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2855 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2856 return Some(called_contract);2857 }2858 }2859 }28602861 None2862 },28632864 _ => None,2865 });28662867 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());28682869 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2870 .map(|i| (fee, i))2871 }2872}287328742875impl<T: Config + Send + Sync> SignedExtension2876 for ChargeTransactionPayment<T>2877where2878 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2879 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2880 T::AccountId: AsRef<[u8]>,2881 T::AccountId: UncheckedFrom<T::Hash>,2882{2883 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2884 type AccountId = T::AccountId;2885 type Call = T::Call;2886 type AdditionalSigned = ();2887 type Pre = (2888 2889 BalanceOf<T>,2890 2891 Self::AccountId,2892 2893 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2894 );2895 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2896 Ok(())2897 }28982899 fn validate(2900 &self,2901 who: &Self::AccountId,2902 call: &Self::Call,2903 info: &DispatchInfoOf<Self::Call>,2904 len: usize,2905 ) -> TransactionValidity {2906 let (fee, _) = self.withdraw_fee(who, call, info, len)?;2907 Ok(ValidTransaction {2908 priority: Self::get_priority(len, info, fee),2909 ..Default::default()2910 })2911 }29122913 fn pre_dispatch(2914 self,2915 who: &Self::AccountId,2916 call: &Self::Call,2917 info: &DispatchInfoOf<Self::Call>,2918 len: usize,2919 ) -> Result<Self::Pre, TransactionValidityError> {2920 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2921 Ok((self.0, who.clone(), imbalance))2922 }29232924 fn post_dispatch(2925 pre: Self::Pre,2926 info: &DispatchInfoOf<Self::Call>,2927 post_info: &PostDispatchInfoOf<Self::Call>,2928 len: usize,2929 _result: &DispatchResult,2930 ) -> Result<(), TransactionValidityError> {2931 let (tip, who, imbalance) = pre;2932 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2933 len as u32,2934 info,2935 post_info,2936 tip,2937 );2938 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2939 Ok(())2940 }2941}29422943