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 codec::{Decode, Encode};17pub use frame_support::{18 construct_runtime, decl_event, decl_module, decl_storage, decl_error,19 dispatch::DispatchResult,20 ensure, fail, parameter_types,21 traits::{22 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23 Randomness, IsSubType,24 },25 weights::{26 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28 WeightToFeePolynomial, DispatchClass,29 },30 StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};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_transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061626364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71 Invalid,72 NFT,73 74 Fungible(DecimalPoints),75 ReFungible,76}7778impl Default for CollectionMode {79 fn default() -> Self {80 Self::Invalid81 }82}8384impl Into<u8> for CollectionMode {85 fn into(self) -> u8 {86 match self {87 CollectionMode::Invalid => 0,88 CollectionMode::NFT => 1,89 CollectionMode::Fungible(_) => 2,90 CollectionMode::ReFungible => 3,91 }92 }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98 Normal,99 WhiteList,100}101impl Default for AccessMode {102 fn default() -> Self {103 Self::Normal104 }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110 ImageURL,111 Unique,112}113impl Default for SchemaVersion {114 fn default() -> Self {115 Self::ImageURL116 }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122 pub owner: AccountId,123 pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct Collection<T: Config> {129 pub id: CollectionId,130 pub owner: T::AccountId,131 pub mode: CollectionMode,132 pub access: AccessMode,133 pub decimal_points: DecimalPoints,134 pub name: Vec<u16>, 135 pub description: Vec<u16>, 136 pub token_prefix: Vec<u8>, 137 pub mint_mode: bool,138 pub offchain_schema: Vec<u8>,139 pub schema_version: SchemaVersion,140 pub sponsor: T::AccountId, 141 pub sponsor_confirmed: bool, 142 pub limits: CollectionLimits<T::BlockNumber>, 143 pub variable_on_chain_schema: Vec<u8>, 144 pub const_on_chain_schema: Vec<u8>, 145}146147#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]148#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]149pub struct NftItemType<AccountId> {150 pub owner: AccountId,151 pub const_data: Vec<u8>,152 pub variable_data: Vec<u8>,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct FungibleItemType {158 pub value: u128,159}160161#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]162#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]163pub struct ReFungibleItemType<AccountId> {164 pub owner: Vec<Ownership<AccountId>>,165 pub const_data: Vec<u8>,166 pub variable_data: Vec<u8>,167}168169170171172173174175176177178179180#[derive(Encode, Decode, Debug, Clone, PartialEq)]181#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]182pub struct CollectionLimits<BlockNumber: Encode + Decode> {183 pub account_token_ownership_limit: u32,184 pub sponsored_data_size: u32,185 186 187 188 pub sponsored_data_rate_limit: Option<BlockNumber>,189 pub token_limit: u32,190191 192 pub sponsor_transfer_timeout: u32,193 pub owner_can_transfer: bool,194 pub owner_can_destroy: bool,195}196197impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {198 fn default() -> Self {199 Self { 200 account_token_ownership_limit: 10_000_000, 201 token_limit: u32::max_value(),202 sponsored_data_size: u32::MAX, 203 sponsored_data_rate_limit: None,204 sponsor_transfer_timeout: 14400,205 owner_can_transfer: true,206 owner_can_destroy: true207 }208 }209}210211#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]212#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]213pub struct ChainLimits {214 pub collection_numbers_limit: u32,215 pub account_token_ownership_limit: u32,216 pub collections_admins_limit: u64,217 pub custom_data_limit: u32,218219 220 pub nft_sponsor_transfer_timeout: u32,221 pub fungible_sponsor_transfer_timeout: u32,222 pub refungible_sponsor_transfer_timeout: u32,223224 225 pub offchain_schema_limit: u32,226 pub variable_on_chain_schema_limit: u32,227 pub const_on_chain_schema_limit: u32,228}229230pub trait WeightInfo {231 fn create_collection() -> Weight;232 fn destroy_collection() -> Weight;233 fn add_to_white_list() -> Weight;234 fn remove_from_white_list() -> Weight;235 fn set_public_access_mode() -> Weight;236 fn set_mint_permission() -> Weight;237 fn change_collection_owner() -> Weight;238 fn add_collection_admin() -> Weight;239 fn remove_collection_admin() -> Weight;240 fn set_collection_sponsor() -> Weight;241 fn confirm_sponsorship() -> Weight;242 fn remove_collection_sponsor() -> Weight;243 fn create_item(s: usize) -> Weight;244 fn burn_item() -> Weight;245 fn transfer() -> Weight;246 fn approve() -> Weight;247 fn transfer_from() -> Weight;248 fn set_offchain_schema() -> Weight;249 fn set_const_on_chain_schema() -> Weight;250 fn set_variable_on_chain_schema() -> Weight;251 fn set_variable_meta_data() -> Weight;252 fn enable_contract_sponsoring() -> Weight;253 fn set_schema_version() -> Weight;254 fn set_chain_limits() -> Weight;255 fn set_contract_sponsoring_rate_limit() -> Weight;256 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;257 fn toggle_contract_white_list() -> Weight;258 fn add_to_contract_white_list() -> Weight;259 fn remove_from_contract_white_list() -> Weight;260 fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266 pub const_data: Vec<u8>,267 pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273 pub value: u128,274}275276#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CreateReFungibleData {279 pub const_data: Vec<u8>,280 pub variable_data: Vec<u8>,281 pub pieces: u128,282}283284#[derive(Encode, Decode, Debug, Clone, PartialEq)]285#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]286pub enum CreateItemData {287 NFT(CreateNftData),288 Fungible(CreateFungibleData),289 ReFungible(CreateReFungibleData),290}291292impl CreateItemData {293 pub fn len(&self) -> usize {294 let len = match self {295 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),296 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),297 _ => 0298 };299 300 return len;301 }302}303304impl From<CreateNftData> for CreateItemData {305 fn from(item: CreateNftData) -> Self {306 CreateItemData::NFT(item)307 }308}309310impl From<CreateReFungibleData> for CreateItemData {311 fn from(item: CreateReFungibleData) -> Self {312 CreateItemData::ReFungible(item)313 }314}315316impl From<CreateFungibleData> for CreateItemData {317 fn from(item: CreateFungibleData) -> Self {318 CreateItemData::Fungible(item)319 }320}321322323decl_error! {324 325 pub enum Error for Module<T: Config> {326 327 TotalCollectionsLimitExceeded,328 329 CollectionDecimalPointLimitExceeded, 330 331 CollectionNameLimitExceeded, 332 333 CollectionDescriptionLimitExceeded, 334 335 CollectionTokenPrefixLimitExceeded,336 337 CollectionNotFound,338 339 TokenNotFound,340 341 AdminNotFound,342 343 NumOverflow, 344 345 AlreadyAdmin, 346 347 NoPermission,348 349 ConfirmUnsetSponsorFail,350 351 PublicMintingNotAllowed,352 353 MustBeTokenOwner,354 355 TokenValueTooLow,356 357 NftSizeLimitExceeded,358 359 ApproveNotFound,360 361 TokenValueNotEnough,362 363 ApproveRequired,364 365 AddresNotInWhiteList,366 367 CollectionAdminsLimitExceeded,368 369 AddressOwnershipLimitExceeded,370 371 EmptyArgument,372 373 TokenConstDataLimitExceeded,374 375 TokenVariableDataLimitExceeded,376 377 NotNftDataUsedToMintNftCollectionToken,378 379 NotFungibleDataUsedToMintFungibleCollectionToken,380 381 NotReFungibleDataUsedToMintReFungibleCollectionToken,382 383 UnexpectedCollectionType,384 385 CantStoreMetadataInFungibleTokens,386 387 CollectionTokenLimitExceeded,388 389 AccountTokenLimitExceeded,390 391 CollectionLimitBoundsExceeded,392 393 OwnerPermissionsCantBeReverted,394 395 SchemaDataLimitExceeded,396 397 WrongRefungiblePieces398 }399}400401pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {402 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;403404 405 type WeightInfo: WeightInfo;406}407408#[cfg(feature = "runtime-benchmarks")]409mod benchmarking;410411412413decl_storage! {414 trait Store for Module<T: Config> as Nft {415416 417 NextCollectionID: CollectionId;418 CreatedCollectionCount: u32;419 ChainVersion: u64;420 ItemListIndex: map hasher(identity) CollectionId => TokenId;421422 423 pub ChainLimit get(fn chain_limit) config(): ChainLimits;424425 426 CollectionCount: u32;427 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;428429 430 pub CollectionById get(fn collection_id) config(): map hasher(identity) CollectionId => Option<Collection<T>> = None;431 pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;432 pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;433434 435 pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;436437 438 pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;439440 441 pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;442 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;443 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;444445 446 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;447448 449 pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;450 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;451 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;452 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;453454 455 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;456457 458 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;459 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;460 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;461 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;462 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 463 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 464 }465 add_extra_genesis {466 build(|config: &GenesisConfig<T>| {467 468 for (_num, _c) in &config.collection_id {469 <Module<T>>::init_collection(_c);470 }471472 for (_num, _c, _i) in &config.nft_item_id {473 <Module<T>>::init_nft_token(*_c, _i);474 }475476 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {477 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);478 }479480 for (_num, _c, _i) in &config.refungible_item_id {481 <Module<T>>::init_refungible_token(*_c, _i);482 }483 })484 }485}486487decl_event!(488 pub enum Event<T>489 where490 AccountId = <T as system::Config>::AccountId,491 {492 493 494 495 496 497 498 499 500 501 Created(CollectionId, u8, AccountId),502503 504 505 506 507 508 509 510 511 512 ItemCreated(CollectionId, TokenId, AccountId),513514 515 516 517 518 519 520 521 ItemDestroyed(CollectionId, TokenId),522523 524 525 526 527 528 529 530 531 532 533 534 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),535 }536);537538decl_module! {539 pub struct Module<T: Config> for enum Call 540 where 541 origin: T::Origin542 {543 fn deposit_event() = default;544 type Error = Error<T>;545546 fn on_initialize(now: T::BlockNumber) -> Weight {547548 if ChainVersion::get() < 2549 {550 let value = NextCollectionID::get();551 CreatedCollectionCount::put(value);552 ChainVersion::put(2);553 }554555 0556 }557558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 #[weight = <T as Config>::WeightInfo::create_collection()]575 pub fn create_collection(origin,576 collection_name: Vec<u16>,577 collection_description: Vec<u16>,578 token_prefix: Vec<u8>,579 mode: CollectionMode) -> DispatchResult {580581 582 let who = ensure_signed(origin)?;583584 let decimal_points = match mode {585 CollectionMode::Fungible(points) => points,586 _ => 0587 };588589 let chain_limit = ChainLimit::get();590591 592 ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);593594 595 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);596 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);597 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);598 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);599600 601 let next_id = CreatedCollectionCount::get()602 .checked_add(1)603 .ok_or(Error::<T>::NumOverflow)?;604605 606 let total = CollectionCount::get()607 .checked_add(1)608 .ok_or(Error::<T>::NumOverflow)?;609610 CreatedCollectionCount::put(next_id);611 CollectionCount::put(total);612613 let limits = CollectionLimits {614 sponsored_data_size: chain_limit.custom_data_limit,615 ..Default::default()616 };617618 619 let new_collection = Collection {620 id: next_id,621 owner: who.clone(),622 name: collection_name,623 mode: mode.clone(),624 mint_mode: false,625 access: AccessMode::Normal,626 description: collection_description,627 decimal_points: decimal_points,628 token_prefix: token_prefix,629 offchain_schema: Vec::new(),630 schema_version: SchemaVersion::ImageURL,631 sponsor: T::AccountId::default(),632 sponsor_confirmed: false,633 variable_on_chain_schema: Vec::new(),634 const_on_chain_schema: Vec::new(),635 limits,636 };637638 639 <CollectionById<T>>::insert(next_id, new_collection);640641 642 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));643644 Ok(())645 }646647 648 649 650 651 652 653 654 655 656 #[weight = <T as Config>::WeightInfo::destroy_collection()]657 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {658659 let sender = ensure_signed(origin)?;660 let collection = Self::get_collection(collection_id)?;661 Self::check_owner_permissions(&collection, sender)?;662 if !collection.limits.owner_can_destroy {663 fail!(Error::<T>::NoPermission);664 }665666 <AddressTokens<T>>::remove_prefix(collection_id);667 <Allowances<T>>::remove_prefix(collection_id);668 <Balance<T>>::remove_prefix(collection_id);669 <ItemListIndex>::remove(collection_id);670 <AdminList<T>>::remove(collection_id);671 <CollectionById<T>>::remove(collection_id);672 <WhiteList<T>>::remove_prefix(collection_id);673674 <NftItemList<T>>::remove_prefix(collection_id);675 <FungibleItemList<T>>::remove_prefix(collection_id);676 <ReFungibleItemList<T>>::remove_prefix(collection_id);677678 <NftTransferBasket<T>>::remove_prefix(collection_id);679 <FungibleTransferBasket<T>>::remove_prefix(collection_id);680 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);681682 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);683684 if CollectionCount::get() > 0685 {686 687 let total = CollectionCount::get()688 .checked_sub(1)689 .ok_or(Error::<T>::NumOverflow)?;690691 CollectionCount::put(total);692 }693694 Ok(())695 }696697 698 699 700 701 702 703 704 705 706 707 708 709 #[weight = <T as Config>::WeightInfo::add_to_white_list()]710 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{711712 let sender = ensure_signed(origin)?;713 let collection = Self::get_collection(collection_id)?;714 Self::check_owner_or_admin_permissions(&collection, sender)?;715716 <WhiteList<T>>::insert(collection_id, address, true);717 718 Ok(())719 }720721 722 723 724 725 726 727 728 729 730 731 732 733 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]734 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{735736 let sender = ensure_signed(origin)?;737 let collection = Self::get_collection(collection_id)?;738 Self::check_owner_or_admin_permissions(&collection, sender)?;739740 <WhiteList<T>>::remove(collection_id, address);741742 Ok(())743 }744745 746 747 748 749 750 751 752 753 754 755 756 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]757 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult758 {759 let sender = ensure_signed(origin)?;760761 let mut target_collection = Self::get_collection(collection_id)?;762 Self::check_owner_permissions(&target_collection, sender)?;763 target_collection.access = mode;764 Self::save_collection(target_collection);765766 Ok(())767 }768769 770 771 772 773 774 775 776 777 778 779 780 781 782 #[weight = <T as Config>::WeightInfo::set_mint_permission()]783 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult784 {785 let sender = ensure_signed(origin)?;786787 let mut target_collection = Self::get_collection(collection_id)?;788 Self::check_owner_permissions(&target_collection, sender)?;789 target_collection.mint_mode = mint_permission;790 Self::save_collection(target_collection);791792 Ok(())793 }794795 796 797 798 799 800 801 802 803 804 805 806 #[weight = <T as Config>::WeightInfo::change_collection_owner()]807 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {808809 let sender = ensure_signed(origin)?;810 let mut target_collection = Self::get_collection(collection_id)?;811 Self::check_owner_permissions(&target_collection, sender)?;812 target_collection.owner = new_owner;813 Self::save_collection(target_collection);814815 Ok(())816 }817818 819 820 821 822 823 824 825 826 827 828 829 830 831 #[weight = <T as Config>::WeightInfo::add_collection_admin()]832 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {833834 let sender = ensure_signed(origin)?;835 let collection = Self::get_collection(collection_id)?;836 Self::check_owner_or_admin_permissions(&collection, sender)?;837 let mut admin_arr: Vec<T::AccountId> = Vec::new();838839 if <AdminList<T>>::contains_key(collection_id)840 {841 admin_arr = <AdminList<T>>::get(collection_id);842 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);843 }844845 846 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);847848 admin_arr.push(new_admin_id);849 <AdminList<T>>::insert(collection_id, admin_arr);850851 Ok(())852 }853854 855 856 857 858 859 860 861 862 863 864 865 866 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]867 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {868869 let sender = ensure_signed(origin)?;870 let collection = Self::get_collection(collection_id)?;871 Self::check_owner_or_admin_permissions(&collection, sender)?;872 ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);873874 let mut admin_arr = <AdminList<T>>::get(collection_id);875 admin_arr.retain(|i| *i != account_id);876 <AdminList<T>>::insert(collection_id, admin_arr);877878 Ok(())879 }880881 882 883 884 885 886 887 888 889 890 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]891 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {892893 let sender = ensure_signed(origin)?;894 let mut target_collection = Self::get_collection(collection_id)?;895 Self::check_owner_permissions(&target_collection, sender)?;896897 target_collection.sponsor = new_sponsor;898 target_collection.sponsor_confirmed = false;899 Self::save_collection(target_collection);900901 Ok(())902 }903904 905 906 907 908 909 910 911 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]912 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {913914 let sender = ensure_signed(origin)?;915916 let mut target_collection = Self::get_collection(collection_id)?;917 ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);918919 target_collection.sponsor_confirmed = true;920 Self::save_collection(target_collection);921922 Ok(())923 }924925 926 927 928 929 930 931 932 933 934 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]935 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {936937 let sender = ensure_signed(origin)?;938939 let mut target_collection = Self::get_collection(collection_id)?;940 Self::check_owner_permissions(&target_collection, sender)?;941942 target_collection.sponsor = T::AccountId::default();943 target_collection.sponsor_confirmed = false;944 Self::save_collection(target_collection);945946 Ok(())947 }948949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972973 #[weight = <T as Config>::WeightInfo::create_item(data.len())]974 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {975976 let sender = ensure_signed(origin)?;977978 let target_collection = Self::get_collection(collection_id)?;979980 Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;981 Self::validate_create_item_args(&target_collection, &data)?;982 Self::create_item_no_validation(&target_collection, owner, data)?;983984 Ok(())985 }986987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1006 .map(|data| { data.len() })1007 .sum())]1008 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10091010 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1011 let sender = ensure_signed(origin)?;10121013 let target_collection = Self::get_collection(collection_id)?;10141015 Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;10161017 for data in &items_data {1018 Self::validate_create_item_args(&target_collection, data)?;1019 }1020 for data in &items_data {1021 Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1022 }10231024 Ok(())1025 }10261027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 #[weight = <T as Config>::WeightInfo::burn_item()]1041 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10421043 let sender = ensure_signed(origin)?;10441045 1046 let target_collection = Self::get_collection(collection_id)?;1047 ensure!(1048 Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1049 (1050 target_collection.limits.owner_can_transfer &&1051 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1052 ),1053 Error::<T>::NoPermission1054 );10551056 if target_collection.access == AccessMode::WhiteList {1057 Self::check_white_list(&target_collection, &sender)?;1058 }10591060 match target_collection.mode1061 {1062 CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1063 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?,1064 CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1065 _ => ()1066 };10671068 1069 Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));10701071 Ok(())1072 }10731074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 #[weight = <T as Config>::WeightInfo::transfer()]1098 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1099 let sender = ensure_signed(origin)?;1100 let collection = Self::get_collection(collection_id)?;11011102 Self::transfer_internal(sender, recipient, &collection, item_id, value)1103 }11041105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 #[weight = <T as Config>::WeightInfo::approve()]1121 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11221123 let sender = ensure_signed(origin)?;1124 let target_collection = Self::get_collection(collection_id)?;11251126 Self::token_exists(&target_collection, item_id, &sender)?;11271128 1129 let bypasses_limits = target_collection.limits.owner_can_transfer &&1130 Self::is_owner_or_admin_permissions(1131 &target_collection,1132 sender.clone(),1133 );11341135 let allowance_limit = if bypasses_limits {1136 None1137 } else if let Some(amount) = Self::owned_amount(1138 sender.clone(),1139 &target_collection,1140 item_id,1141 ) {1142 Some(amount)1143 } else {1144 fail!(Error::<T>::NoPermission);1145 };11461147 if target_collection.access == AccessMode::WhiteList {1148 Self::check_white_list(&target_collection, &sender)?;1149 Self::check_white_list(&target_collection, &spender)?;1150 }11511152 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1153 let mut allowance: u128 = amount;1154 if allowance_exists {1155 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1156 }1157 if let Some(limit) = allowance_limit {1158 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1159 }1160 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11611162 Ok(())1163 }1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 #[weight = <T as Config>::WeightInfo::transfer_from()]1185 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11861187 let sender = ensure_signed(origin)?;1188 let target_collection = Self::get_collection(collection_id)?;11891190 let mut appoved_transfer = false;11911192 1193 let mut approval: u128 = 0;1194 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1195 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1196 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1197 appoved_transfer = true;1198 }11991200 1201 Self::is_correct_transfer(&target_collection, &recipient)?;12021203 1204 ensure!(1205 appoved_transfer || 1206 (1207 target_collection.limits.owner_can_transfer &&1208 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1209 ),1210 Error::<T>::NoPermission1211 );12121213 if target_collection.access == AccessMode::WhiteList {1214 Self::check_white_list(&target_collection, &sender)?;1215 Self::check_white_list(&target_collection, &recipient)?;1216 }12171218 1219 if approval.checked_sub(value).unwrap_or(0) > 0 {1220 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1221 }1222 else {1223 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1224 }12251226 match target_collection.mode1227 {1228 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from, recipient)?,1229 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1230 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient)?,1231 _ => ()1232 };12331234 Ok(())1235 }12361237 1238 12391240 1241 1242 1243 12441245 12461247 12481249 1250 12511252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1265 pub fn set_variable_meta_data (1266 origin,1267 collection_id: CollectionId,1268 item_id: TokenId,1269 data: Vec<u8>1270 ) -> DispatchResult {1271 let sender = ensure_signed(origin)?;1272 1273 let target_collection = Self::get_collection(collection_id)?;1274 Self::token_exists(&target_collection, item_id, &sender)?;12751276 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12771278 1279 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1280 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1281 Error::<T>::NoPermission);12821283 match target_collection.mode1284 {1285 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1286 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1287 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1288 _ => fail!(Error::<T>::UnexpectedCollectionType)1289 };12901291 Ok(())1292 }1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 #[weight = <T as Config>::WeightInfo::set_schema_version()]1309 pub fn set_schema_version(1310 origin,1311 collection_id: CollectionId,1312 version: SchemaVersion1313 ) -> DispatchResult {1314 let sender = ensure_signed(origin)?;1315 let mut target_collection = Self::get_collection(collection_id)?;1316 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1317 target_collection.schema_version = version;1318 Self::save_collection(target_collection);13191320 Ok(())1321 }13221323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1336 pub fn set_offchain_schema(1337 origin,1338 collection_id: CollectionId,1339 schema: Vec<u8>1340 ) -> DispatchResult {1341 let sender = ensure_signed(origin)?;1342 let mut target_collection = Self::get_collection(collection_id)?;1343 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13441345 1346 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13471348 target_collection.offchain_schema = schema;1349 Self::save_collection(target_collection);13501351 Ok(())1352 }13531354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1367 pub fn set_const_on_chain_schema (1368 origin,1369 collection_id: CollectionId,1370 schema: Vec<u8>1371 ) -> DispatchResult {1372 let sender = ensure_signed(origin)?;1373 let mut target_collection = Self::get_collection(collection_id)?;1374 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13751376 1377 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13781379 target_collection.const_on_chain_schema = schema;1380 Self::save_collection(target_collection);13811382 Ok(())1383 }13841385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1398 pub fn set_variable_on_chain_schema (1399 origin,1400 collection_id: CollectionId,1401 schema: Vec<u8>1402 ) -> DispatchResult {1403 let sender = ensure_signed(origin)?;1404 let mut target_collection = Self::get_collection(collection_id)?;1405 Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14061407 1408 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14091410 target_collection.variable_on_chain_schema = schema;1411 Self::save_collection(target_collection);14121413 Ok(())1414 }14151416 1417 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1418 pub fn set_chain_limits(1419 origin,1420 limits: ChainLimits1421 ) -> DispatchResult {14221423 #[cfg(not(feature = "runtime-benchmarks"))]1424 ensure_root(origin)?;14251426 <ChainLimit>::put(limits);1427 Ok(())1428 }14291430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1442 pub fn enable_contract_sponsoring(1443 origin,1444 contract_address: T::AccountId,1445 enable: bool1446 ) -> DispatchResult {14471448 let sender = ensure_signed(origin)?;14491450 #[cfg(feature = "runtime-benchmarks")]1451 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453 Self::ensure_contract_owned(sender, &contract_address)?;14541455 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1456 Ok(())1457 }14581459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1477 pub fn set_contract_sponsoring_rate_limit(1478 origin,1479 contract_address: T::AccountId,1480 rate_limit: T::BlockNumber1481 ) -> DispatchResult {1482 let sender = ensure_signed(origin)?;14831484 #[cfg(feature = "runtime-benchmarks")]1485 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14861487 Self::ensure_contract_owned(sender, &contract_address)?;1488 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1489 Ok(())1490 }14911492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1504 pub fn toggle_contract_white_list(1505 origin,1506 contract_address: T::AccountId,1507 enable: bool1508 ) -> DispatchResult {1509 let sender = ensure_signed(origin)?;15101511 #[cfg(feature = "runtime-benchmarks")]1512 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15131514 Self::ensure_contract_owned(sender, &contract_address)?;1515 <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1516 Ok(())1517 }1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1531 pub fn add_to_contract_white_list(1532 origin,1533 contract_address: T::AccountId,1534 account_address: T::AccountId1535 ) -> DispatchResult {1536 let sender = ensure_signed(origin)?;15371538 #[cfg(feature = "runtime-benchmarks")]1539 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1540 1541 Self::ensure_contract_owned(sender, &contract_address)?; 1542 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1543 Ok(())1544 }15451546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1558 pub fn remove_from_contract_white_list(1559 origin,1560 contract_address: T::AccountId,1561 account_address: T::AccountId1562 ) -> DispatchResult {1563 let sender = ensure_signed(origin)?;15641565 #[cfg(feature = "runtime-benchmarks")]1566 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15671568 Self::ensure_contract_owned(sender, &contract_address)?;1569 <ContractWhiteList<T>>::remove(contract_address, account_address);1570 Ok(())1571 }15721573 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1574 pub fn set_collection_limits(1575 origin,1576 collection_id: u32,1577 new_limits: CollectionLimits<T::BlockNumber>,1578 ) -> DispatchResult {1579 let sender = ensure_signed(origin)?;1580 let mut target_collection = Self::get_collection(collection_id)?;1581 Self::check_owner_permissions(&target_collection, sender.clone())?;1582 let old_limits = target_collection.limits;1583 let chain_limits = ChainLimit::get();15841585 1586 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1587 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1588 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1589 Error::<T>::CollectionLimitBoundsExceeded);15901591 1592 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1593 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15941595 ensure!(1596 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1597 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1598 Error::<T>::OwnerPermissionsCantBeReverted,1599 );16001601 target_collection.limits = new_limits;1602 Self::save_collection(target_collection);16031604 Ok(())1605 } 1606 }1607}16081609impl<T: Config> Module<T> {16101611 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &Collection<T>, item_id: TokenId, value: u128) -> DispatchResult {1612 1613 Self::is_correct_transfer(target_collection, &recipient)?;16141615 1616 ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1617 Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1618 Error::<T>::NoPermission);16191620 if target_collection.access == AccessMode::WhiteList {1621 Self::check_white_list(target_collection, &sender)?;1622 Self::check_white_list(target_collection, &recipient)?;1623 }16241625 match target_collection.mode1626 {1627 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1628 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1629 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1630 _ => ()1631 };16321633 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));16341635 Ok(())1636 }163716381639 fn is_correct_transfer(collection: &Collection<T>, recipient: &T::AccountId) -> DispatchResult {1640 let collection_id = collection.id;16411642 1643 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1644 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1645 1646 Ok(())1647 }16481649 fn can_create_items_in_collection(collection: &Collection<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {1650 let collection_id = collection.id;16511652 1653 let total_items: u32 = ItemListIndex::get(collection_id);1654 let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1655 ensure!(collection.limits.token_limit > total_items, Error::<T>::CollectionTokenLimitExceeded);1656 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);16571658 if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1659 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1660 Self::check_white_list(collection, owner)?;1661 Self::check_white_list(collection, sender)?;1662 }16631664 Ok(())1665 }16661667 fn validate_create_item_args(target_collection: &Collection<T>, data: &CreateItemData) -> DispatchResult {1668 match target_collection.mode1669 {1670 CollectionMode::NFT => {1671 if let CreateItemData::NFT(data) = data {1672 1673 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1674 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1675 } else {1676 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1677 }1678 },1679 CollectionMode::Fungible(_) => {1680 if let CreateItemData::Fungible(_) = data {1681 } else {1682 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1683 }1684 },1685 CollectionMode::ReFungible => {1686 if let CreateItemData::ReFungible(data) = data {16871688 1689 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1690 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16911692 1693 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1694 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1695 } else {1696 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1697 }1698 },1699 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1700 };17011702 Ok(())1703 }17041705 fn create_item_no_validation(collection: &Collection<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1706 let collection_id = collection.id;17071708 match data1709 {1710 CreateItemData::NFT(data) => {1711 let item = NftItemType {1712 owner: owner.clone(),1713 const_data: data.const_data,1714 variable_data: data.variable_data1715 };17161717 Self::add_nft_item(collection, item)?;1718 },1719 CreateItemData::Fungible(data) => {1720 Self::add_fungible_item(collection, &owner, data.value)?;1721 },1722 CreateItemData::ReFungible(data) => {1723 let mut owner_list = Vec::new();1724 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17251726 let item = ReFungibleItemType {1727 owner: owner_list,1728 const_data: data.const_data,1729 variable_data: data.variable_data1730 };17311732 Self::add_refungible_item(collection, item)?;1733 }1734 };17351736 1737 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17381739 Ok(())1740 }17411742 fn add_fungible_item(collection: &Collection<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1743 let collection_id = collection.id;17441745 1746 let mut balance: u128 = 0;1747 if <FungibleItemList<T>>::contains_key(collection_id, owner) {1748 balance = <FungibleItemList<T>>::get(collection_id, owner).value;1749 } 17501751 1752 let item = FungibleItemType {1753 value: balance + value1754 };1755 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17561757 1758 let new_balance = <Balance<T>>::get(collection_id, owner)1759 .checked_add(value)1760 .ok_or(Error::<T>::NumOverflow)?;1761 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17621763 Ok(())1764 }17651766 fn add_refungible_item(collection: &Collection<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1767 let collection_id = collection.id;17681769 let current_index = <ItemListIndex>::get(collection_id)1770 .checked_add(1)1771 .ok_or(Error::<T>::NumOverflow)?;1772 let itemcopy = item.clone();17731774 let value = item.owner.first().unwrap().fraction;1775 let owner = item.owner.first().unwrap().owner.clone();17761777 Self::add_token_index(collection_id, current_index, &owner)?;17781779 <ItemListIndex>::insert(collection_id, current_index);1780 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17811782 1783 let new_balance = <Balance<T>>::get(collection_id, &owner)1784 .checked_add(value)1785 .ok_or(Error::<T>::NumOverflow)?;1786 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17871788 Ok(())1789 }17901791 fn add_nft_item(collection: &Collection<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1792 let collection_id = collection.id;17931794 let current_index = <ItemListIndex>::get(collection_id)1795 .checked_add(1)1796 .ok_or(Error::<T>::NumOverflow)?;17971798 let item_owner = item.owner.clone();1799 Self::add_token_index(collection_id, current_index, &item.owner)?;18001801 <ItemListIndex>::insert(collection_id, current_index);1802 <NftItemList<T>>::insert(collection_id, current_index, item);18031804 1805 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1806 .checked_add(1)1807 .ok_or(Error::<T>::NumOverflow)?;1808 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18091810 Ok(())1811 }18121813 fn burn_refungible_item(1814 collection: &Collection<T>,1815 item_id: TokenId,1816 owner: &T::AccountId,1817 ) -> DispatchResult {1818 let collection_id = collection.id;18191820 ensure!(1821 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1822 Error::<T>::TokenNotFound1823 );1824 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1825 let rft_balance = token1826 .owner1827 .iter()1828 .filter(|&i| i.owner == *owner)1829 .next()1830 .unwrap();1831 Self::remove_token_index(collection_id, item_id, owner)?;18321833 1834 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1835 .checked_sub(rft_balance.fraction)1836 .ok_or(Error::<T>::NumOverflow)?;1837 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18381839 1840 let index = token1841 .owner1842 .iter()1843 .position(|i| i.owner == *owner)1844 .unwrap();1845 token.owner.remove(index);1846 let owner_count = token.owner.len();18471848 1849 if owner_count == 0 {1850 <ReFungibleItemList<T>>::remove(collection_id, item_id);1851 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1852 }1853 else {1854 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1855 }18561857 Ok(())1858 }18591860 fn burn_nft_item(collection: &Collection<T>, item_id: TokenId) -> DispatchResult {1861 let collection_id = collection.id;18621863 ensure!(1864 <NftItemList<T>>::contains_key(collection_id, item_id),1865 Error::<T>::TokenNotFound1866 );1867 let item = <NftItemList<T>>::get(collection_id, item_id);1868 Self::remove_token_index(collection_id, item_id, &item.owner)?;18691870 1871 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1872 .checked_sub(1)1873 .ok_or(Error::<T>::NumOverflow)?;1874 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1875 <NftItemList<T>>::remove(collection_id, item_id);1876 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18771878 Ok(())1879 }18801881 fn burn_fungible_item(owner: &T::AccountId, collection: &Collection<T>, value: u128) -> DispatchResult {1882 let collection_id = collection.id;18831884 ensure!(1885 <FungibleItemList<T>>::contains_key(collection_id, owner),1886 Error::<T>::TokenNotFound1887 );1888 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1889 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18901891 1892 let new_balance = <Balance<T>>::get(collection_id, owner)1893 .checked_sub(value)1894 .ok_or(Error::<T>::NumOverflow)?;1895 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18961897 if balance.value - value > 0 {1898 balance.value -= value;1899 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900 }1901 else {1902 <FungibleItemList<T>>::remove(collection_id, owner);1903 }19041905 Ok(())1906 }19071908 pub fn get_collection(collection_id: CollectionId) -> Result<Collection<T>, sp_runtime::DispatchError> {1909 Ok(<CollectionById<T>>::get(collection_id)1910 .ok_or(Error::<T>::CollectionNotFound)?)1911 }19121913 fn save_collection(collection: Collection<T>) {1914 <CollectionById<T>>::insert(collection.id, collection);1915 }19161917 fn check_owner_permissions(target_collection: &Collection<T>, subject: T::AccountId) -> DispatchResult {1918 ensure!(1919 subject == target_collection.owner,1920 Error::<T>::NoPermission1921 );19221923 Ok(())1924 }19251926 fn is_owner_or_admin_permissions(collection: &Collection<T>, subject: T::AccountId) -> bool {1927 let mut result: bool = subject == collection.owner;1928 let exists = <AdminList<T>>::contains_key(collection.id);19291930 if !result & exists {1931 if <AdminList<T>>::get(collection.id).contains(&subject) {1932 result = true1933 }1934 }19351936 result1937 }19381939 fn check_owner_or_admin_permissions(1940 collection: &Collection<T>,1941 subject: T::AccountId,1942 ) -> DispatchResult {1943 let result = Self::is_owner_or_admin_permissions(collection, subject.clone());19441945 ensure!(1946 result,1947 Error::<T>::NoPermission1948 );1949 Ok(())1950 }19511952 fn owned_amount(1953 subject: T::AccountId,1954 target_collection: &Collection<T>,1955 item_id: TokenId,1956 ) -> Option<u128> {1957 let collection_id = target_collection.id;19581959 match target_collection.mode {1960 CollectionMode::NFT => {1961 if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1962 return Some(1)1963 }1964 None1965 },1966 CollectionMode::Fungible(_) => {1967 if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1968 return Some(<FungibleItemList<T>>::get(collection_id, &subject)1969 .value);1970 }1971 None1972 },1973 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1974 .owner1975 .iter()1976 .find(|i| i.owner == subject)1977 .map(|i| i.fraction),1978 CollectionMode::Invalid => None,1979 }1980 }19811982 fn is_item_owner(subject: T::AccountId, target_collection: &Collection<T>, item_id: TokenId) -> bool {1983 let collection_id = target_collection.id;19841985 match target_collection.mode {1986 CollectionMode::NFT => {1987 <NftItemList<T>>::get(collection_id, item_id).owner == subject1988 }1989 CollectionMode::Fungible(_) => {1990 <FungibleItemList<T>>::contains_key(collection_id, &subject)1991 }1992 CollectionMode::ReFungible => {1993 <ReFungibleItemList<T>>::get(collection_id, item_id)1994 .owner1995 .iter()1996 .any(|i| i.owner == subject)1997 }1998 CollectionMode::Invalid => false,1999 }2000 }20012002 fn check_white_list(collection: &Collection<T>, address: &T::AccountId) -> DispatchResult {2003 let collection_id = collection.id;20042005 let mes = Error::<T>::AddresNotInWhiteList;2006 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20072008 Ok(())2009 }20102011 2012 2013 fn token_exists(2014 target_collection: &Collection<T>,2015 item_id: TokenId,2016 owner: &T::AccountId2017 ) -> DispatchResult {2018 let collection_id = target_collection.id;2019 let exists = match target_collection.mode2020 {2021 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2022 CollectionMode::Fungible(_) => <FungibleItemList<T>>::contains_key(collection_id, owner),2023 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2024 _ => false2025 };20262027 ensure!(exists == true, Error::<T>::TokenNotFound);2028 Ok(())2029 }20302031 fn transfer_fungible(2032 collection: &Collection<T>,2033 value: u128,2034 owner: &T::AccountId,2035 recipient: &T::AccountId,2036 ) -> DispatchResult {2037 let collection_id = collection.id;2038 Self::token_exists(&collection, 0, owner)?;20392040 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2041 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20422043 2044 Self::add_fungible_item(collection, recipient, value)?;20452046 2047 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20482049 2050 if balance.value == value {2051 <FungibleItemList<T>>::remove(collection_id, owner);2052 }2053 else {2054 balance.value -= value;2055 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2056 }20572058 Ok(())2059 }20602061 fn transfer_refungible(2062 collection: &Collection<T>,2063 item_id: TokenId,2064 value: u128,2065 owner: T::AccountId,2066 new_owner: T::AccountId,2067 ) -> DispatchResult {2068 let collection_id = collection.id;2069 Self::token_exists(collection, item_id, &owner)?;20702071 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2072 let item = full_item2073 .owner2074 .iter()2075 .filter(|i| i.owner == owner)2076 .next()2077 .ok_or(Error::<T>::NumOverflow)?;2078 let amount = item.fraction;20792080 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20812082 2083 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2084 .checked_sub(value)2085 .ok_or(Error::<T>::NumOverflow)?;2086 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20872088 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2089 .checked_add(value)2090 .ok_or(Error::<T>::NumOverflow)?;2091 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20922093 let old_owner = item.owner.clone();2094 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20952096 2097 if amount == value && !new_owner_has_account {2098 2099 2100 let mut new_full_item = full_item.clone();2101 new_full_item2102 .owner2103 .iter_mut()2104 .find(|i| i.owner == owner)2105 .unwrap()2106 .owner = new_owner.clone();2107 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21082109 2110 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2111 } else {2112 let mut new_full_item = full_item.clone();2113 new_full_item2114 .owner2115 .iter_mut()2116 .find(|i| i.owner == owner)2117 .unwrap()2118 .fraction -= value;21192120 2121 if new_owner_has_account {2122 2123 new_full_item2124 .owner2125 .iter_mut()2126 .find(|i| i.owner == new_owner)2127 .unwrap()2128 .fraction += value;2129 } else {2130 2131 new_full_item.owner.push(Ownership {2132 owner: new_owner.clone(),2133 fraction: value,2134 });2135 Self::add_token_index(collection_id, item_id, &new_owner)?;2136 }21372138 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2139 }21402141 Ok(())2142 }21432144 fn transfer_nft(2145 collection: &Collection<T>,2146 item_id: TokenId,2147 sender: T::AccountId,2148 new_owner: T::AccountId,2149 ) -> DispatchResult {2150 let collection_id = collection.id;2151 Self::token_exists(&collection, item_id, &sender)?;21522153 let mut item = <NftItemList<T>>::get(collection_id, item_id);21542155 ensure!(2156 sender == item.owner,2157 Error::<T>::MustBeTokenOwner2158 );21592160 2161 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2162 .checked_sub(1)2163 .ok_or(Error::<T>::NumOverflow)?;2164 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21652166 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2167 .checked_add(1)2168 .ok_or(Error::<T>::NumOverflow)?;2169 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21702171 2172 let old_owner = item.owner.clone();2173 item.owner = new_owner.clone();2174 <NftItemList<T>>::insert(collection_id, item_id, item);21752176 2177 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21782179 Ok(())2180 }2181 2182 fn set_re_fungible_variable_data(2183 collection: &Collection<T>,2184 item_id: TokenId,2185 data: Vec<u8>2186 ) -> DispatchResult {2187 let collection_id = collection.id;2188 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21892190 item.variable_data = data;21912192 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21932194 Ok(())2195 }21962197 fn set_nft_variable_data(2198 collection: &Collection<T>,2199 item_id: TokenId,2200 data: Vec<u8>2201 ) -> DispatchResult {2202 let collection_id = collection.id;2203 let mut item = <NftItemList<T>>::get(collection_id, item_id);2204 2205 item.variable_data = data;22062207 <NftItemList<T>>::insert(collection_id, item_id, item);2208 2209 Ok(())2210 }22112212 fn init_collection(item: &Collection<T>) {2213 2214 assert!(2215 item.decimal_points <= MAX_DECIMAL_POINTS,2216 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2217 );2218 assert!(2219 item.name.len() <= 64,2220 "Collection name can not be longer than 63 char"2221 );2222 assert!(2223 item.name.len() <= 256,2224 "Collection description can not be longer than 255 char"2225 );2226 assert!(2227 item.token_prefix.len() <= 16,2228 "Token prefix can not be longer than 15 char"2229 );22302231 2232 let next_id = CreatedCollectionCount::get()2233 .checked_add(1)2234 .unwrap();22352236 CreatedCollectionCount::put(next_id);2237 }22382239 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2240 let current_index = <ItemListIndex>::get(collection_id)2241 .checked_add(1)2242 .unwrap();22432244 let item_owner = item.owner.clone();2245 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22462247 <ItemListIndex>::insert(collection_id, current_index);22482249 2250 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2251 .checked_add(1)2252 .unwrap();2253 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2254 }22552256 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2257 let current_index = <ItemListIndex>::get(collection_id)2258 .checked_add(1)2259 .unwrap();22602261 Self::add_token_index(collection_id, current_index, owner).unwrap();22622263 <ItemListIndex>::insert(collection_id, current_index);22642265 2266 let new_balance = <Balance<T>>::get(collection_id, owner)2267 .checked_add(item.value)2268 .unwrap();2269 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2270 }22712272 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2273 let current_index = <ItemListIndex>::get(collection_id)2274 .checked_add(1)2275 .unwrap();22762277 let value = item.owner.first().unwrap().fraction;2278 let owner = item.owner.first().unwrap().owner.clone();22792280 Self::add_token_index(collection_id, current_index, &owner).unwrap();22812282 <ItemListIndex>::insert(collection_id, current_index);22832284 2285 let new_balance = <Balance<T>>::get(collection_id, &owner)2286 .checked_add(value)2287 .unwrap();2288 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2289 }22902291 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2292 2293 if <AccountItemCount<T>>::contains_key(owner) {22942295 2296 let count = <AccountItemCount<T>>::get(owner);2297 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22982299 <AccountItemCount<T>>::insert(owner.clone(), count2300 .checked_add(1)2301 .ok_or(Error::<T>::NumOverflow)?);2302 }2303 else {2304 <AccountItemCount<T>>::insert(owner.clone(), 1);2305 }23062307 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2308 if list_exists {2309 let mut list = <AddressTokens<T>>::get(collection_id, owner);2310 let item_contains = list.contains(&item_index.clone());23112312 if !item_contains {2313 list.push(item_index.clone());2314 }23152316 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2317 } else {2318 let mut itm = Vec::new();2319 itm.push(item_index.clone());2320 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2321 }23222323 Ok(())2324 }23252326 fn remove_token_index(2327 collection_id: CollectionId,2328 item_index: TokenId,2329 owner: &T::AccountId,2330 ) -> DispatchResult {23312332 2333 <AccountItemCount<T>>::insert(owner.clone(), 2334 <AccountItemCount<T>>::get(owner)2335 .checked_sub(1)2336 .ok_or(Error::<T>::NumOverflow)?);233723382339 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2340 if list_exists {2341 let mut list = <AddressTokens<T>>::get(collection_id, owner);2342 let item_contains = list.contains(&item_index.clone());23432344 if item_contains {2345 list.retain(|&item| item != item_index);2346 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2347 }2348 }23492350 Ok(())2351 }23522353 fn move_token_index(2354 collection_id: CollectionId,2355 item_index: TokenId,2356 old_owner: &T::AccountId,2357 new_owner: &T::AccountId,2358 ) -> DispatchResult {2359 Self::remove_token_index(collection_id, item_index, old_owner)?;2360 Self::add_token_index(collection_id, item_index, new_owner)?;23612362 Ok(())2363 }2364 2365 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2366 if <ContractOwner<T>>::contains_key(contract.clone()) {2367 let owner = <ContractOwner<T>>::get(contract);2368 ensure!(account == owner, Error::<T>::NoPermission);2369 } else {2370 fail!(Error::<T>::NoPermission);2371 }23722373 Ok(())2374 }2375}2376237723782379238023812382pub type Multiplier = FixedU128;23832384type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2385238623872388#[derive(Encode, Decode, Clone, Eq, PartialEq)]2389pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23902391impl<T: Config + Send + Sync> sp_std::fmt::Debug 2392 for ChargeTransactionPayment<T>2393{2394 #[cfg(feature = "std")]2395 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2396 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2397 }2398 #[cfg(not(feature = "std"))]2399 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2400 Ok(())2401 }2402}24032404impl<T: Config> ChargeTransactionPayment<T>2405where2406 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2407 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2408 T::AccountId: AsRef<[u8]>,2409 T::AccountId: UncheckedFrom<T::Hash>,2410{2411 fn traditional_fee(2412 len: usize,2413 info: &DispatchInfoOf<T::Call>,2414 tip: BalanceOf<T>,2415 ) -> BalanceOf<T>2416 where2417 T::Call: Dispatchable<Info = DispatchInfo>,2418 {2419 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2420 }24212422 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2423 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2424 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2425 let len_saturation = max_block_length as u64 / (len as u64).max(1);2426 let coefficient: BalanceOf<T> = weight_saturation2427 .min(len_saturation)2428 .saturated_into::<BalanceOf<T>>();2429 final_fee2430 .saturating_mul(coefficient)2431 .saturated_into::<TransactionPriority>()2432 }24332434 fn withdraw_fee(2435 &self,2436 who: &T::AccountId,2437 call: &T::Call,2438 info: &DispatchInfoOf<T::Call>,2439 len: usize,2440 ) -> Result<2441 (2442 BalanceOf<T>,2443 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2444 ),2445 TransactionValidityError,2446 > {2447 let tip = self.0;24482449 2450 2451 2452 2453 2454 2455 2456 let fee = Self::traditional_fee(len, info, tip);24572458 2459 if fee.is_zero() {2460 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2461 .map(|i| (fee, i));2462 }24632464 2465 2466 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2467 Some(Call::create_item(collection_id, _owner, _properties)) => {2468 let collection = <CollectionById<T>>::get(collection_id)?;24692470 2471 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24722473 let limit = collection.limits.sponsor_transfer_timeout;2474 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2475 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2476 let limit_time = last_tx_block + limit.into();2477 if block_number <= limit_time {2478 return None;2479 }2480 }2481 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);24822483 2484 if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2485 (collection.sponsor_confirmed)2486 {2487 Some(collection.sponsor)2488 } else {2489 None2490 }2491 }2492 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2493 let collection = <CollectionById<T>>::get(collection_id)?;2494 2495 let mut sponsor_transfer = false;2496 if collection.sponsor_confirmed {24972498 let collection_limits = collection.limits;2499 let collection_mode = collection.mode;2500 2501 2502 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2503 sponsor_transfer = match collection_mode {2504 CollectionMode::NFT => {2505 2506 2507 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2508 collection_limits.sponsor_transfer_timeout2509 } else {2510 ChainLimit::get().nft_sponsor_transfer_timeout2511 };2512 2513 let mut sponsored = true;2514 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2515 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2516 let limit_time = last_tx_block + limit.into();2517 if block_number <= limit_time {2518 sponsored = false;2519 }2520 }2521 if sponsored {2522 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2523 }25242525 sponsored2526 }2527 CollectionMode::Fungible(_) => {2528 2529 2530 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2531 collection_limits.sponsor_transfer_timeout2532 } else {2533 ChainLimit::get().fungible_sponsor_transfer_timeout2534 };2535 2536 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2537 let mut sponsored = true;2538 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2539 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2540 let limit_time = last_tx_block + limit.into();2541 if block_number <= limit_time {2542 sponsored = false;2543 }2544 }2545 if sponsored {2546 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2547 }25482549 sponsored2550 }2551 CollectionMode::ReFungible => {2552 2553 2554 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2555 collection_limits.sponsor_transfer_timeout2556 } else {2557 ChainLimit::get().refungible_sponsor_transfer_timeout2558 };2559 2560 let mut sponsored = true;2561 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2562 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2563 let limit_time = last_tx_block + limit.into();2564 if block_number <= limit_time {2565 sponsored = false;2566 }2567 }2568 if sponsored {2569 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2570 }25712572 sponsored2573 }2574 _ => {2575 false2576 },2577 };2578 }25792580 if !sponsor_transfer {2581 None2582 } else {2583 Some(collection.sponsor)2584 }2585 }25862587 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2588 let mut sponsor_metadata_changes = false;25892590 let collection = <CollectionById<T>>::get(collection_id)?;25912592 if2593 collection.sponsor_confirmed &&2594 2595 2596 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2597 data.len() <= collection.limits.sponsored_data_size as usize2598 {2599 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2600 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26012602 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2603 .map(|last_block| block_number - last_block > rate_limit)2604 .unwrap_or(true) 2605 {2606 sponsor_metadata_changes = true;2607 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2608 }2609 }2610 }26112612 if !sponsor_metadata_changes {2613 None2614 } else {2615 Some(collection.sponsor)2616 }2617 }26182619 _ => None,2620 })();26212622 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2623 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26242625 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26262627 let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2628 && <ContractOwner<T>>::get(called_contract.clone()) == *who;2629 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2630 2631 if !owned_contract && white_list_enabled {2632 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2633 return Err(InvalidTransaction::Call.into());2634 }2635 }2636 },2637 _ => {},2638 }26392640 2641 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26422643 2644 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26452646 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2647 &who,2648 code_hash,2649 salt,2650 );2651 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26522653 None2654 },26552656 2657 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {26582659 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2660 &who,2661 &T::Hashing::hash(&_code),2662 _salt,2663 );26642665 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26662667 T::AccountId::default()2668 }26692670 2671 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26722673 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26742675 let mut sponsor_transfer = false;2676 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2677 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2678 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2679 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2680 let limit_time = last_tx_block + rate_limit;26812682 if block_number >= limit_time {2683 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2684 sponsor_transfer = true;2685 }2686 } else {2687 sponsor_transfer = false;2688 }2689 2690 if sponsor_transfer {2691 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2692 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2693 return Some(called_contract);2694 }2695 }2696 }26972698 None2699 },27002701 _ => None,2702 });27032704 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());27052706 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2707 .map(|i| (fee, i))2708 }2709}271027112712impl<T: Config + Send + Sync> SignedExtension2713 for ChargeTransactionPayment<T>2714where2715 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2716 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2717 T::AccountId: AsRef<[u8]>,2718 T::AccountId: UncheckedFrom<T::Hash>,2719{2720 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2721 type AccountId = T::AccountId;2722 type Call = T::Call;2723 type AdditionalSigned = ();2724 type Pre = (2725 2726 BalanceOf<T>,2727 2728 Self::AccountId,2729 2730 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2731 );2732 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2733 Ok(())2734 }27352736 fn validate(2737 &self,2738 who: &Self::AccountId,2739 call: &Self::Call,2740 info: &DispatchInfoOf<Self::Call>,2741 len: usize,2742 ) -> TransactionValidity {2743 let (fee, _) = self.withdraw_fee(who, call, info, len)?;2744 Ok(ValidTransaction {2745 priority: Self::get_priority(len, info, fee),2746 ..Default::default()2747 })2748 }27492750 fn pre_dispatch(2751 self,2752 who: &Self::AccountId,2753 call: &Self::Call,2754 info: &DispatchInfoOf<Self::Call>,2755 len: usize,2756 ) -> Result<Self::Pre, TransactionValidityError> {2757 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2758 Ok((self.0, who.clone(), imbalance))2759 }27602761 fn post_dispatch(2762 pre: Self::Pre,2763 info: &DispatchInfoOf<Self::Call>,2764 post_info: &PostDispatchInfoOf<Self::Call>,2765 len: usize,2766 _result: &DispatchResult,2767 ) -> Result<(), TransactionValidityError> {2768 let (tip, who, imbalance) = pre;2769 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2770 len as u32,2771 info,2772 post_info,2773 tip,2774 );2775 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2776 Ok(())2777 }2778}27792780