123456#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910extern crate alloc;1112pub use serde::{Serialize, Deserialize};1314use core::ops::{Deref, DerefMut};15use core::cell::RefCell;16use 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, WithdrawReasons,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 transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_core::H160;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_ethereum::EthereumTransactionSender;49use pallet_transaction_payment::OnChargeTransaction;5051#[cfg(test)]52mod mock;5354#[cfg(test)]55mod tests;5657mod default_weights;58mod eth;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;65pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;66pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;67pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6869707172pub type CollectionId = u32;73pub type TokenId = u32;74pub type DecimalPoints = u8;7576#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]77#[derive(Serialize, Deserialize)]78pub enum CollectionMode {79 Invalid,80 NFT,81 82 Fungible(DecimalPoints),83 ReFungible,84}8586impl Default for CollectionMode {87 fn default() -> Self {88 Self::Invalid89 }90}9192impl Into<u8> for CollectionMode {93 fn into(self) -> u8 {94 match self {95 CollectionMode::Invalid => 0,96 CollectionMode::NFT => 1,97 CollectionMode::Fungible(_) => 2,98 CollectionMode::ReFungible => 3,99 }100 }101}102103#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]104#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]105pub enum AccessMode {106 Normal,107 WhiteList,108}109impl Default for AccessMode {110 fn default() -> Self {111 Self::Normal112 }113}114115#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]116#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]117pub enum SchemaVersion {118 ImageURL,119 Unique,120}121impl Default for SchemaVersion {122 fn default() -> Self {123 Self::ImageURL124 }125}126127#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]128#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]129pub struct Ownership<AccountId> {130 pub owner: AccountId,131 pub fraction: u128,132}133134#[derive(Encode, Decode, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub enum SponsorshipState<AccountId> {137 138 Disabled,139 Unconfirmed(AccountId),140 141 Confirmed(AccountId),142}143144impl<AccountId> SponsorshipState<AccountId> {145 fn sponsor(&self) -> Option<&AccountId> {146 match self {147 Self::Confirmed(sponsor) => Some(sponsor),148 _ => None,149 }150 }151152 fn pending_sponsor(&self) -> Option<&AccountId> {153 match self {154 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),155 _ => None,156 }157 }158159 fn confirmed(&self) -> bool {160 matches!(self, Self::Confirmed(_))161 }162}163164impl<T> Default for SponsorshipState<T> {165 fn default() -> Self {166 Self::Disabled167 }168}169170#[derive(Encode, Decode, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct Collection<T: Config> {173 pub owner: T::AccountId,174 pub mode: CollectionMode,175 pub access: AccessMode,176 pub decimal_points: DecimalPoints,177 pub name: Vec<u16>, 178 pub description: Vec<u16>, 179 pub token_prefix: Vec<u8>, 180 pub mint_mode: bool,181 pub offchain_schema: Vec<u8>,182 pub schema_version: SchemaVersion,183 pub sponsorship: SponsorshipState<T::AccountId>,184 pub limits: CollectionLimits<T::BlockNumber>, 185 pub variable_on_chain_schema: Vec<u8>, 186 pub const_on_chain_schema: Vec<u8>, 187}188189pub struct CollectionHandle<T: Config> {190 pub id: CollectionId,191 collection: Collection<T>,192 logs: eth::log::LogRecorder,193 evm_address: H160,194 gas_limit: RefCell<u64>,195}196impl<T: Config> CollectionHandle<T> {197 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {198 <CollectionById<T>>::get(id)199 .map(|collection| Self {200 id,201 collection,202 logs: eth::log::LogRecorder::default(),203 evm_address: eth::collection_id_to_address(id),204 gas_limit: RefCell::new(gas_limit),205 })206 }207 pub fn get(id: CollectionId) -> Option<Self> {208 Self::get_with_gas_limit(id, u64::MAX)209 }210 pub fn gas_left(&self) -> u64 {211 *self.gas_limit.borrow()212 }213 pub fn consume_gas(&self, gas: u64) -> DispatchResult {214 let mut gas_limit = self.gas_limit.borrow_mut();215 if *gas_limit < gas {216 fail!(Error::<T>::OutOfGas);217 }218 *gas_limit -= gas;219 Ok(())220 }221 pub fn log(&self, log: impl evm_coder::ToLog) {222 self.logs.log(log.to_log(self.evm_address))223 }224 pub fn into_inner(self) -> Collection<T> {225 self.collection.clone()226 }227}228229impl<T: Config> Deref for CollectionHandle<T> {230 type Target = Collection<T>;231232 fn deref(&self) -> &Self::Target {233 &self.collection234 }235}236237impl<T: Config> DerefMut for CollectionHandle<T> {238 fn deref_mut(&mut self) -> &mut Self::Target {239 &mut self.collection240 }241}242243#[derive(Encode, Decode, Debug, Clone, PartialEq)]244#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]245pub struct NftItemType<AccountId> {246 pub owner: AccountId,247 pub const_data: Vec<u8>,248 pub variable_data: Vec<u8>,249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct FungibleItemType {254 pub value: u128,255}256257#[derive(Encode, Decode, Debug, Clone, PartialEq)]258#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]259pub struct ReFungibleItemType<AccountId> {260 pub owner: Vec<Ownership<AccountId>>,261 pub const_data: Vec<u8>,262 pub variable_data: Vec<u8>,263}264265266267268269270271272273274275276#[derive(Encode, Decode, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CollectionLimits<BlockNumber: Encode + Decode> {279 pub account_token_ownership_limit: u32,280 pub sponsored_data_size: u32,281 282 283 284 pub sponsored_data_rate_limit: Option<BlockNumber>,285 pub token_limit: u32,286287 288 pub sponsor_transfer_timeout: u32,289 pub owner_can_transfer: bool,290 pub owner_can_destroy: bool,291}292293impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {294 fn default() -> Self {295 Self { 296 account_token_ownership_limit: 10_000_000, 297 token_limit: u32::max_value(),298 sponsored_data_size: u32::MAX, 299 sponsored_data_rate_limit: None,300 sponsor_transfer_timeout: 14400,301 owner_can_transfer: true,302 owner_can_destroy: true303 }304 }305}306307#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]308#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]309pub struct ChainLimits {310 pub collection_numbers_limit: u32,311 pub account_token_ownership_limit: u32,312 pub collections_admins_limit: u64,313 pub custom_data_limit: u32,314315 316 pub nft_sponsor_transfer_timeout: u32,317 pub fungible_sponsor_transfer_timeout: u32,318 pub refungible_sponsor_transfer_timeout: u32,319320 321 pub offchain_schema_limit: u32,322 pub variable_on_chain_schema_limit: u32,323 pub const_on_chain_schema_limit: u32,324}325326pub trait WeightInfo {327 fn create_collection() -> Weight;328 fn destroy_collection() -> Weight;329 fn add_to_white_list() -> Weight;330 fn remove_from_white_list() -> Weight;331 fn set_public_access_mode() -> Weight;332 fn set_mint_permission() -> Weight;333 fn change_collection_owner() -> Weight;334 fn add_collection_admin() -> Weight;335 fn remove_collection_admin() -> Weight;336 fn set_collection_sponsor() -> Weight;337 fn confirm_sponsorship() -> Weight;338 fn remove_collection_sponsor() -> Weight;339 fn create_item(s: usize) -> Weight;340 fn burn_item() -> Weight;341 fn transfer() -> Weight;342 fn approve() -> Weight;343 fn transfer_from() -> Weight;344 fn set_offchain_schema() -> Weight;345 fn set_const_on_chain_schema() -> Weight;346 fn set_variable_on_chain_schema() -> Weight;347 fn set_variable_meta_data() -> Weight;348 fn enable_contract_sponsoring() -> Weight;349 fn set_schema_version() -> Weight;350 fn set_chain_limits() -> Weight;351 fn set_contract_sponsoring_rate_limit() -> Weight;352 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;353 fn toggle_contract_white_list() -> Weight;354 fn add_to_contract_white_list() -> Weight;355 fn remove_from_contract_white_list() -> Weight;356 fn set_collection_limits() -> Weight;357}358359#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub struct CreateNftData {362 pub const_data: Vec<u8>,363 pub variable_data: Vec<u8>,364}365366#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]367#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]368pub struct CreateFungibleData {369 pub value: u128,370}371372#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]373#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]374pub struct CreateReFungibleData {375 pub const_data: Vec<u8>,376 pub variable_data: Vec<u8>,377 pub pieces: u128,378}379380#[derive(Encode, Decode, Debug, Clone, PartialEq)]381#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]382pub enum CreateItemData {383 NFT(CreateNftData),384 Fungible(CreateFungibleData),385 ReFungible(CreateReFungibleData),386}387388impl CreateItemData {389 pub fn len(&self) -> usize {390 let len = match self {391 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),392 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),393 _ => 0394 };395 396 return len;397 }398}399400impl From<CreateNftData> for CreateItemData {401 fn from(item: CreateNftData) -> Self {402 CreateItemData::NFT(item)403 }404}405406impl From<CreateReFungibleData> for CreateItemData {407 fn from(item: CreateReFungibleData) -> Self {408 CreateItemData::ReFungible(item)409 }410}411412impl From<CreateFungibleData> for CreateItemData {413 fn from(item: CreateFungibleData) -> Self {414 CreateItemData::Fungible(item)415 }416}417418419decl_error! {420 421 pub enum Error for Module<T: Config> {422 423 TotalCollectionsLimitExceeded,424 425 CollectionDecimalPointLimitExceeded, 426 427 CollectionNameLimitExceeded, 428 429 CollectionDescriptionLimitExceeded, 430 431 CollectionTokenPrefixLimitExceeded,432 433 CollectionNotFound,434 435 TokenNotFound,436 437 AdminNotFound,438 439 NumOverflow, 440 441 AlreadyAdmin, 442 443 NoPermission,444 445 ConfirmUnsetSponsorFail,446 447 PublicMintingNotAllowed,448 449 MustBeTokenOwner,450 451 TokenValueTooLow,452 453 NftSizeLimitExceeded,454 455 ApproveNotFound,456 457 TokenValueNotEnough,458 459 ApproveRequired,460 461 AddresNotInWhiteList,462 463 CollectionAdminsLimitExceeded,464 465 AddressOwnershipLimitExceeded,466 467 EmptyArgument,468 469 TokenConstDataLimitExceeded,470 471 TokenVariableDataLimitExceeded,472 473 NotNftDataUsedToMintNftCollectionToken,474 475 NotFungibleDataUsedToMintFungibleCollectionToken,476 477 NotReFungibleDataUsedToMintReFungibleCollectionToken,478 479 UnexpectedCollectionType,480 481 CantStoreMetadataInFungibleTokens,482 483 CollectionTokenLimitExceeded,484 485 AccountTokenLimitExceeded,486 487 CollectionLimitBoundsExceeded,488 489 OwnerPermissionsCantBeReverted,490 491 SchemaDataLimitExceeded,492 493 WrongRefungiblePieces,494 495 BadCreateRefungibleCall,496 497 OutOfGas,498 }499}500501pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {502 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;503504 505 type WeightInfo: WeightInfo;506507 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;508 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;509 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;510511 type CrossAccountId: CrossAccountId<Self::AccountId>;512 type Currency: Currency<Self::AccountId>;513 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;514 type TreasuryAccountId: Get<Self::AccountId>;515516 type EthereumChainId: Get<u64>;517 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;518}519520#[cfg(feature = "runtime-benchmarks")]521mod benchmarking;522523524525526527528529530531532533534535536537538539540541542543544545546547decl_storage! {548 trait Store for Module<T: Config> as Nft {549550 551 552 CreatedCollectionCount: u32;553 554 ChainVersion: u64;555 556 557 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;558 559560 561 pub ChainLimit get(fn chain_limit) config(): ChainLimits;562 563564 565 566 567 DestroyedCollectionCount: u32;568 569 570 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;571 572573 574 575 576 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;577 578 579 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;580 581 582 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;583 584585 586 587 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;588589 590 591 592 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;593594 595 596 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;597 598 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;599 600 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;601 602603 604 605 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;606 607608 609 610 611 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;612 613 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;614 615 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;616 617 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;618 619620 621 622 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;623 624 625 626 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;627 628 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;629 630 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;631 632 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;633 634 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 635 636 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 637 638 }639 add_extra_genesis {640 build(|config: &GenesisConfig<T>| {641 642 for (_num, _c) in &config.collection_id {643 <Module<T>>::init_collection(_c);644 }645646 for (_num, _c, _i) in &config.nft_item_id {647 <Module<T>>::init_nft_token(*_c, _i);648 }649650 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {651 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);652 }653654 for (_num, _c, _i) in &config.refungible_item_id {655 <Module<T>>::init_refungible_token(*_c, _i);656 }657 })658 }659}660661decl_event!(662 pub enum Event<T>663 where664 AccountId = <T as frame_system::Config>::AccountId,665 CrossAccountId = <T as Config>::CrossAccountId,666 {667 668 669 670 671 672 673 674 675 676 CollectionCreated(CollectionId, u8, AccountId),677678 679 680 681 682 683 684 685 686 687 ItemCreated(CollectionId, TokenId, CrossAccountId),688689 690 691 692 693 694 695 696 ItemDestroyed(CollectionId, TokenId),697698 699 700 701 702 703 704 705 706 707 708 709 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),710711 712 713 714 715 716 717 718 719 720 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),721 }722);723724decl_module! {725 pub struct Module<T: Config> for enum Call 726 where 727 origin: T::Origin728 {729 fn deposit_event() = default;730 type Error = Error<T>;731732 fn on_initialize(now: T::BlockNumber) -> Weight {733 0734 }735736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 #[weight = <T as Config>::WeightInfo::create_collection()]753 #[transactional]754 pub fn create_collection(origin,755 collection_name: Vec<u16>,756 collection_description: Vec<u16>,757 token_prefix: Vec<u8>,758 mode: CollectionMode) -> DispatchResult {759760 761 let who = ensure_signed(origin)?;762763 764 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();765 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(766 &T::TreasuryAccountId::get(),767 T::CollectionCreationPrice::get(),768 ));769 <T as Config>::Currency::settle(770 &who,771 imbalance,772 WithdrawReasons::TRANSFER,773 ExistenceRequirement::KeepAlive,774 ).map_err(|_| Error::<T>::NoPermission)?;775776 let decimal_points = match mode {777 CollectionMode::Fungible(points) => points,778 _ => 0779 };780781 let chain_limit = ChainLimit::get();782783 let created_count = CreatedCollectionCount::get();784 let destroyed_count = DestroyedCollectionCount::get();785786 787 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);788789 790 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);791 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);792 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);793 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);794795 796 let next_id = created_count797 .checked_add(1)798 .ok_or(Error::<T>::NumOverflow)?;799800 CreatedCollectionCount::put(next_id);801802 let limits = CollectionLimits {803 sponsored_data_size: chain_limit.custom_data_limit,804 ..Default::default()805 };806807 808 let new_collection = Collection {809 owner: who.clone(),810 name: collection_name,811 mode: mode.clone(),812 mint_mode: false,813 access: AccessMode::Normal,814 description: collection_description,815 decimal_points: decimal_points,816 token_prefix: token_prefix,817 offchain_schema: Vec::new(),818 schema_version: SchemaVersion::ImageURL,819 sponsorship: SponsorshipState::Disabled,820 variable_on_chain_schema: Vec::new(),821 const_on_chain_schema: Vec::new(),822 limits,823 };824825 826 <CollectionById<T>>::insert(next_id, new_collection);827828 829 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));830831 Ok(())832 }833834 835 836 837 838 839 840 841 842 843 #[weight = <T as Config>::WeightInfo::destroy_collection()]844 #[transactional]845 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {846847 let sender = ensure_signed(origin)?;848 let collection = Self::get_collection(collection_id)?;849 Self::check_owner_permissions(&collection, &sender)?;850 if !collection.limits.owner_can_destroy {851 fail!(Error::<T>::NoPermission);852 }853854 <AddressTokens<T>>::remove_prefix(collection_id);855 <Allowances<T>>::remove_prefix(collection_id);856 <Balance<T>>::remove_prefix(collection_id);857 <ItemListIndex>::remove(collection_id);858 <AdminList<T>>::remove(collection_id);859 <CollectionById<T>>::remove(collection_id);860 <WhiteList<T>>::remove_prefix(collection_id);861862 <NftItemList<T>>::remove_prefix(collection_id);863 <FungibleItemList<T>>::remove_prefix(collection_id);864 <ReFungibleItemList<T>>::remove_prefix(collection_id);865866 <NftTransferBasket<T>>::remove_prefix(collection_id);867 <FungibleTransferBasket<T>>::remove_prefix(collection_id);868 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);869870 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);871872 DestroyedCollectionCount::put(DestroyedCollectionCount::get()873 .checked_add(1)874 .ok_or(Error::<T>::NumOverflow)?);875876 Ok(())877 }878879 880 881 882 883 884 885 886 887 888 889 890 891 #[weight = <T as Config>::WeightInfo::add_to_white_list()]892 #[transactional]893 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{894895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897898 Self::toggle_white_list_internal(899 &sender,900 &collection,901 &address,902 true,903 )?;904905 Ok(())906 }907908 909 910 911 912 913 914 915 916 917 918 919 920 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]921 #[transactional]922 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{923924 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);925 let collection = Self::get_collection(collection_id)?;926927 Self::toggle_white_list_internal(928 &sender,929 &collection,930 &address,931 false,932 )?;933934 Ok(())935 }936937 938 939 940 941 942 943 944 945 946 947 948 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]949 #[transactional]950 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult951 {952 let sender = ensure_signed(origin)?;953954 let mut target_collection = Self::get_collection(collection_id)?;955 Self::check_owner_permissions(&target_collection, &sender)?;956 target_collection.access = mode;957 Self::save_collection(target_collection);958959 Ok(())960 }961962 963 964 965 966 967 968 969 970 971 972 973 974 975 #[weight = <T as Config>::WeightInfo::set_mint_permission()]976 #[transactional]977 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult978 {979 let sender = ensure_signed(origin)?;980981 let mut target_collection = Self::get_collection(collection_id)?;982 Self::check_owner_permissions(&target_collection, &sender)?;983 target_collection.mint_mode = mint_permission;984 Self::save_collection(target_collection);985986 Ok(())987 }988989 990 991 992 993 994 995 996 997 998 999 1000 #[weight = <T as Config>::WeightInfo::change_collection_owner()]1001 #[transactional]1002 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {10031004 let sender = ensure_signed(origin)?;1005 let mut target_collection = Self::get_collection(collection_id)?;1006 Self::check_owner_permissions(&target_collection, &sender)?;1007 target_collection.owner = new_owner;1008 Self::save_collection(target_collection);10091010 Ok(())1011 }10121013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 #[weight = <T as Config>::WeightInfo::add_collection_admin()]1027 #[transactional]1028 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1029 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030 let collection = Self::get_collection(collection_id)?;1031 Self::check_owner_or_admin_permissions(&collection, &sender)?;1032 let mut admin_arr = <AdminList<T>>::get(collection_id);10331034 match admin_arr.binary_search(&new_admin_id) {1035 Ok(_) => {},1036 Err(idx) => {1037 let limits = ChainLimit::get();1038 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1039 admin_arr.insert(idx, new_admin_id);1040 <AdminList<T>>::insert(collection_id, admin_arr);1041 }1042 }1043 Ok(())1044 }10451046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1059 #[transactional]1060 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1061 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1062 let collection = Self::get_collection(collection_id)?;1063 Self::check_owner_or_admin_permissions(&collection, &sender)?;1064 let mut admin_arr = <AdminList<T>>::get(collection_id);10651066 match admin_arr.binary_search(&account_id) {1067 Ok(idx) => {1068 admin_arr.remove(idx);1069 <AdminList<T>>::insert(collection_id, admin_arr);1070 },1071 Err(_) => {}1072 }1073 Ok(())1074 }10751076 1077 1078 1079 1080 1081 1082 1083 1084 1085 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1086 #[transactional]1087 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1088 let sender = ensure_signed(origin)?;1089 let mut target_collection = Self::get_collection(collection_id)?;1090 Self::check_owner_permissions(&target_collection, &sender)?;10911092 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1093 Self::save_collection(target_collection);10941095 Ok(())1096 }10971098 1099 1100 1101 1102 1103 1104 1105 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1106 #[transactional]1107 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1108 let sender = ensure_signed(origin)?;11091110 let mut target_collection = Self::get_collection(collection_id)?;1111 ensure!(1112 target_collection.sponsorship.pending_sponsor() == Some(&sender),1113 Error::<T>::ConfirmUnsetSponsorFail1114 );11151116 target_collection.sponsorship = SponsorshipState::Confirmed(sender);1117 Self::save_collection(target_collection);11181119 Ok(())1120 }11211122 1123 1124 1125 1126 1127 1128 1129 1130 1131 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1132 #[transactional]1133 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1134 let sender = ensure_signed(origin)?;11351136 let mut target_collection = Self::get_collection(collection_id)?;1137 Self::check_owner_permissions(&target_collection, &sender)?;11381139 target_collection.sponsorship = SponsorshipState::Disabled;1140 Self::save_collection(target_collection);11411142 Ok(())1143 }11441145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 11681169 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1170 #[transactional]1171 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1172 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1173 let collection = Self::get_collection(collection_id)?;11741175 Self::create_item_internal(&sender, &collection, &owner, data)?;11761177 Self::submit_logs(collection)?;1178 Ok(())1179 }11801181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1200 .map(|data| { data.len() })1201 .sum())]1202 #[transactional]1203 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {12041205 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1206 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1207 let collection = Self::get_collection(collection_id)?;12081209 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;12101211 Self::submit_logs(collection)?;1212 Ok(())1213 }12141215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 #[weight = <T as Config>::WeightInfo::burn_item()]1229 #[transactional]1230 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12311232 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1233 let target_collection = Self::get_collection(collection_id)?;12341235 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12361237 Self::submit_logs(target_collection)?;1238 Ok(())1239 }12401241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 #[weight = <T as Config>::WeightInfo::transfer()]1265 #[transactional]1266 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1267 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1268 let collection = Self::get_collection(collection_id)?;12691270 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12711272 Self::submit_logs(collection)?;1273 Ok(())1274 }12751276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 #[weight = <T as Config>::WeightInfo::approve()]1292 #[transactional]1293 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1294 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1295 let collection = Self::get_collection(collection_id)?;12961297 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12981299 Self::submit_logs(collection)?;1300 Ok(())1301 }1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 #[weight = <T as Config>::WeightInfo::transfer_from()]1323 #[transactional]1324 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1325 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1326 let collection = Self::get_collection(collection_id)?;13271328 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13291330 Self::submit_logs(collection)?;1331 Ok(())1332 }1333 1334 1335 1336 1337 13381339 13401341 13421343 1344 13451346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1359 #[transactional]1360 pub fn set_variable_meta_data (1361 origin,1362 collection_id: CollectionId,1363 item_id: TokenId,1364 data: Vec<u8>1365 ) -> DispatchResult {1366 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1367 1368 let collection = Self::get_collection(collection_id)?;13691370 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13711372 Ok(())1373 }1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 #[weight = <T as Config>::WeightInfo::set_schema_version()]1390 #[transactional]1391 pub fn set_schema_version(1392 origin,1393 collection_id: CollectionId,1394 version: SchemaVersion1395 ) -> DispatchResult {1396 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1397 let mut target_collection = Self::get_collection(collection_id)?;1398 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1399 target_collection.schema_version = version;1400 Self::save_collection(target_collection);14011402 Ok(())1403 }14041405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1418 #[transactional]1419 pub fn set_offchain_schema(1420 origin,1421 collection_id: CollectionId,1422 schema: Vec<u8>1423 ) -> DispatchResult {1424 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1425 let mut target_collection = Self::get_collection(collection_id)?;1426 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14271428 1429 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14301431 target_collection.offchain_schema = schema;1432 Self::save_collection(target_collection);14331434 Ok(())1435 }14361437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1450 #[transactional]1451 pub fn set_const_on_chain_schema (1452 origin,1453 collection_id: CollectionId,1454 schema: Vec<u8>1455 ) -> DispatchResult {1456 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1457 let mut target_collection = Self::get_collection(collection_id)?;1458 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14591460 1461 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14621463 target_collection.const_on_chain_schema = schema;1464 Self::save_collection(target_collection);14651466 Ok(())1467 }14681469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1482 #[transactional]1483 pub fn set_variable_on_chain_schema (1484 origin,1485 collection_id: CollectionId,1486 schema: Vec<u8>1487 ) -> DispatchResult {1488 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1489 let mut target_collection = Self::get_collection(collection_id)?;1490 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14911492 1493 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14941495 target_collection.variable_on_chain_schema = schema;1496 Self::save_collection(target_collection);14971498 Ok(())1499 }15001501 1502 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1503 #[transactional]1504 pub fn set_chain_limits(1505 origin,1506 limits: ChainLimits1507 ) -> DispatchResult {15081509 #[cfg(not(feature = "runtime-benchmarks"))]1510 ensure_root(origin)?;15111512 <ChainLimit>::put(limits);1513 Ok(())1514 }15151516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1528 #[transactional]1529 pub fn enable_contract_sponsoring(1530 origin,1531 contract_address: T::AccountId,1532 enable: bool1533 ) -> DispatchResult {15341535 let sender = ensure_signed(origin)?;15361537 #[cfg(feature = "runtime-benchmarks")]1538 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15391540 Self::ensure_contract_owned(sender, &contract_address)?;15411542 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1543 Ok(())1544 }15451546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1564 #[transactional]1565 pub fn set_contract_sponsoring_rate_limit(1566 origin,1567 contract_address: T::AccountId,1568 rate_limit: T::BlockNumber1569 ) -> DispatchResult {1570 let sender = ensure_signed(origin)?;15711572 #[cfg(feature = "runtime-benchmarks")]1573 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15741575 Self::ensure_contract_owned(sender, &contract_address)?;1576 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1577 Ok(())1578 }15791580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1592 #[transactional]1593 pub fn toggle_contract_white_list(1594 origin,1595 contract_address: T::AccountId,1596 enable: bool1597 ) -> DispatchResult {1598 let sender = ensure_signed(origin)?;15991600 #[cfg(feature = "runtime-benchmarks")]1601 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16021603 Self::ensure_contract_owned(sender, &contract_address)?;1604 if enable {1605 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1606 } else {1607 <ContractWhiteListEnabled<T>>::remove(contract_address);1608 }1609 Ok(())1610 }1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1624 #[transactional]1625 pub fn add_to_contract_white_list(1626 origin,1627 contract_address: T::AccountId,1628 account_address: T::AccountId1629 ) -> DispatchResult {1630 let sender = ensure_signed(origin)?;16311632 #[cfg(feature = "runtime-benchmarks")]1633 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1634 1635 Self::ensure_contract_owned(sender, &contract_address)?; 1636 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1637 Ok(())1638 }16391640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1652 #[transactional]1653 pub fn remove_from_contract_white_list(1654 origin,1655 contract_address: T::AccountId,1656 account_address: T::AccountId1657 ) -> DispatchResult {1658 let sender = ensure_signed(origin)?;16591660 #[cfg(feature = "runtime-benchmarks")]1661 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16621663 Self::ensure_contract_owned(sender, &contract_address)?;1664 <ContractWhiteList<T>>::remove(contract_address, account_address);1665 Ok(())1666 }16671668 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1669 #[transactional]1670 pub fn set_collection_limits(1671 origin,1672 collection_id: u32,1673 new_limits: CollectionLimits<T::BlockNumber>,1674 ) -> DispatchResult {1675 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1676 let mut target_collection = Self::get_collection(collection_id)?;1677 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1678 let old_limits = &target_collection.limits;1679 let chain_limits = ChainLimit::get();16801681 1682 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1683 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1684 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1685 Error::<T>::CollectionLimitBoundsExceeded);16861687 1688 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1689 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16901691 ensure!(1692 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1693 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1694 Error::<T>::OwnerPermissionsCantBeReverted,1695 );16961697 target_collection.limits = new_limits;1698 Self::save_collection(target_collection);16991700 Ok(())1701 } 1702 }1703}17041705impl<T: Config> Module<T> {1706 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1707 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1708 Self::validate_create_item_args(&collection, &data)?;1709 Self::create_item_no_validation(&collection, owner, data)?;17101711 Ok(())1712 }17131714 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1715 target_collection.consume_gas(2000000)?;1716 1717 Self::is_correct_transfer(target_collection, &recipient)?;17181719 1720 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1721 Self::is_owner_or_admin_permissions(target_collection, &sender),1722 Error::<T>::NoPermission);17231724 if target_collection.access == AccessMode::WhiteList {1725 Self::check_white_list(target_collection, &sender)?;1726 Self::check_white_list(target_collection, &recipient)?;1727 }17281729 match target_collection.mode1730 {1731 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1732 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1733 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1734 _ => ()1735 };17361737 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17381739 Ok(())1740 }17411742 pub fn approve_internal(1743 sender: &T::CrossAccountId,1744 spender: &T::CrossAccountId,1745 collection: &CollectionHandle<T>,1746 item_id: TokenId,1747 amount: u1281748 ) -> DispatchResult {1749 collection.consume_gas(2000000)?;1750 Self::token_exists(&collection, item_id)?;17511752 1753 let bypasses_limits = collection.limits.owner_can_transfer &&1754 Self::is_owner_or_admin_permissions(1755 &collection,1756 &sender,1757 );17581759 let allowance_limit = if bypasses_limits {1760 None1761 } else if let Some(amount) = Self::owned_amount(1762 &sender,1763 &collection,1764 item_id,1765 ) {1766 Some(amount)1767 } else {1768 fail!(Error::<T>::NoPermission);1769 };17701771 if collection.access == AccessMode::WhiteList {1772 Self::check_white_list(&collection, &sender)?;1773 Self::check_white_list(&collection, &spender)?;1774 }17751776 let allowance: u128 = amount1777 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1778 .ok_or(Error::<T>::NumOverflow)?;1779 if let Some(limit) = allowance_limit {1780 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1781 }1782 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17831784 if matches!(collection.mode, CollectionMode::NFT) {1785 1786 collection.log(ERC721Events::Approval {1787 owner: *sender.as_eth(),1788 approved: *spender.as_eth(),1789 token_id: item_id.into(),1790 });1791 }17921793 if matches!(collection.mode, CollectionMode::Fungible(_)) {1794 1795 collection.log(ERC20Events::Approval {1796 owner: *sender.as_eth(),1797 spender: *spender.as_eth(),1798 value: allowance.into()1799 });1800 }18011802 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1803 Ok(())1804 }18051806 pub fn transfer_from_internal(1807 sender: &T::CrossAccountId,1808 from: &T::CrossAccountId,1809 recipient: &T::CrossAccountId,1810 collection: &CollectionHandle<T>,1811 item_id: TokenId,1812 amount: u128,1813 ) -> DispatchResult {1814 collection.consume_gas(2000000)?;1815 1816 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));18171818 1819 Self::is_correct_transfer(&collection, &recipient)?;18201821 1822 ensure!(1823 approval >= amount || 1824 (1825 collection.limits.owner_can_transfer &&1826 Self::is_owner_or_admin_permissions(&collection, &sender)1827 ),1828 Error::<T>::NoPermission1829 );18301831 if collection.access == AccessMode::WhiteList {1832 Self::check_white_list(&collection, &sender)?;1833 Self::check_white_list(&collection, &recipient)?;1834 }18351836 1837 let allowance = approval.saturating_sub(amount);1838 if allowance > 0 {1839 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1840 } else {1841 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1842 }18431844 match collection.mode {1845 CollectionMode::NFT => {1846 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1847 }1848 CollectionMode::Fungible(_) => {1849 Self::transfer_fungible(&collection, amount, &from, &recipient)?1850 }1851 CollectionMode::ReFungible => {1852 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1853 }1854 _ => ()1855 };18561857 if matches!(collection.mode, CollectionMode::Fungible(_)) {1858 collection.log(ERC20Events::Approval {1859 owner: *from.as_eth(),1860 spender: *sender.as_eth(),1861 value: allowance.into()1862 });1863 }18641865 Ok(())1866 }18671868 pub fn set_variable_meta_data_internal(1869 sender: &T::CrossAccountId,1870 collection: &CollectionHandle<T>, 1871 item_id: TokenId,1872 data: Vec<u8>,1873 ) -> DispatchResult {1874 Self::token_exists(&collection, item_id)?;18751876 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18771878 1879 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1880 Self::is_owner_or_admin_permissions(&collection, &sender),1881 Error::<T>::NoPermission);18821883 match collection.mode1884 {1885 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1886 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1887 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1888 _ => fail!(Error::<T>::UnexpectedCollectionType)1889 };18901891 Ok(())1892 }18931894 pub fn create_multiple_items_internal(1895 sender: &T::CrossAccountId,1896 collection: &CollectionHandle<T>,1897 owner: &T::CrossAccountId,1898 items_data: Vec<CreateItemData>,1899 ) -> DispatchResult {1900 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;19011902 for data in &items_data {1903 Self::validate_create_item_args(&collection, data)?;1904 }1905 for data in &items_data {1906 Self::create_item_no_validation(&collection, owner, data.clone())?;1907 }19081909 Ok(())1910 }19111912 pub fn burn_item_internal(1913 sender: &T::CrossAccountId,1914 collection: &CollectionHandle<T>,1915 item_id: TokenId,1916 value: u128,1917 ) -> DispatchResult {1918 ensure!(1919 Self::is_item_owner(&sender, &collection, item_id) ||1920 (1921 collection.limits.owner_can_transfer &&1922 Self::is_owner_or_admin_permissions(&collection, &sender)1923 ),1924 Error::<T>::NoPermission1925 );19261927 if collection.access == AccessMode::WhiteList {1928 Self::check_white_list(&collection, &sender)?;1929 }19301931 match collection.mode1932 {1933 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1934 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1935 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1936 _ => ()1937 };19381939 Ok(())1940 }19411942 pub fn toggle_white_list_internal(1943 sender: &T::CrossAccountId,1944 collection: &CollectionHandle<T>,1945 address: &T::CrossAccountId,1946 whitelisted: bool,1947 ) -> DispatchResult {1948 Self::check_owner_or_admin_permissions(&collection, &sender)?;19491950 if whitelisted {1951 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1952 } else {1953 <WhiteList<T>>::remove(collection.id, address.as_sub());1954 }19551956 Ok(())1957 }19581959 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1960 let collection_id = collection.id;19611962 1963 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1964 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1965 1966 Ok(())1967 }19681969 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1970 let collection_id = collection.id;19711972 1973 let total_items: u32 = ItemListIndex::get(collection_id)1974 .checked_add(amount)1975 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1976 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1977 .checked_add(amount)1978 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1979 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1980 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);19811982 if !Self::is_owner_or_admin_permissions(collection, &sender) {1983 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1984 Self::check_white_list(collection, owner)?;1985 Self::check_white_list(collection, sender)?;1986 }19871988 Ok(())1989 }19901991 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1992 match target_collection.mode1993 {1994 CollectionMode::NFT => {1995 if let CreateItemData::NFT(data) = data {1996 1997 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1998 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1999 } else {2000 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);2001 }2002 },2003 CollectionMode::Fungible(_) => {2004 if let CreateItemData::Fungible(_) = data {2005 } else {2006 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);2007 }2008 },2009 CollectionMode::ReFungible => {2010 if let CreateItemData::ReFungible(data) = data {20112012 2013 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);2014 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);20152016 2017 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2018 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2019 } else {2020 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2021 }2022 },2023 _ => { fail!(Error::<T>::UnexpectedCollectionType); }2024 };20252026 Ok(())2027 }20282029 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2030 match data2031 {2032 CreateItemData::NFT(data) => {2033 let item = NftItemType {2034 owner: owner.clone(),2035 const_data: data.const_data,2036 variable_data: data.variable_data2037 };20382039 Self::add_nft_item(collection, item)?;2040 },2041 CreateItemData::Fungible(data) => {2042 Self::add_fungible_item(collection, &owner, data.value)?;2043 },2044 CreateItemData::ReFungible(data) => {2045 let mut owner_list = Vec::new();2046 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20472048 let item = ReFungibleItemType {2049 owner: owner_list,2050 const_data: data.const_data,2051 variable_data: data.variable_data2052 };20532054 Self::add_refungible_item(collection, item)?;2055 }2056 };20572058 Ok(())2059 }20602061 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2062 let collection_id = collection.id;20632064 2065 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20662067 2068 let item = FungibleItemType {2069 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2070 };2071 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20722073 2074 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2075 .checked_add(value)2076 .ok_or(Error::<T>::NumOverflow)?;2077 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20782079 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2080 Ok(())2081 }20822083 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2084 let collection_id = collection.id;20852086 let current_index = <ItemListIndex>::get(collection_id)2087 .checked_add(1)2088 .ok_or(Error::<T>::NumOverflow)?;2089 let itemcopy = item.clone();20902091 ensure!(2092 item.owner.len() == 1,2093 Error::<T>::BadCreateRefungibleCall,2094 );2095 let item_owner = item.owner.first().expect("only one owner is defined");20962097 let value = item_owner.fraction;2098 let owner = item_owner.owner.clone();20992100 Self::add_token_index(collection_id, current_index, &owner)?;21012102 <ItemListIndex>::insert(collection_id, current_index);2103 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);21042105 2106 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2107 .checked_add(value)2108 .ok_or(Error::<T>::NumOverflow)?;2109 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21102111 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2112 Ok(())2113 }21142115 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2116 let collection_id = collection.id;21172118 let current_index = <ItemListIndex>::get(collection_id)2119 .checked_add(1)2120 .ok_or(Error::<T>::NumOverflow)?;21212122 let item_owner = item.owner.clone();2123 Self::add_token_index(collection_id, current_index, &item.owner)?;21242125 <ItemListIndex>::insert(collection_id, current_index);2126 <NftItemList<T>>::insert(collection_id, current_index, item);21272128 2129 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2130 .checked_add(1)2131 .ok_or(Error::<T>::NumOverflow)?;2132 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21332134 collection.log(ERC721Events::Transfer {2135 from: H160::default(),2136 to: *item_owner.as_eth(),2137 token_id: current_index.into(),2138 });2139 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2140 Ok(())2141 }21422143 fn burn_refungible_item(2144 collection: &CollectionHandle<T>,2145 item_id: TokenId,2146 owner: &T::CrossAccountId,2147 ) -> DispatchResult {2148 let collection_id = collection.id;21492150 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2151 .ok_or(Error::<T>::TokenNotFound)?;2152 let rft_balance = token2153 .owner2154 .iter()2155 .find(|&i| i.owner == *owner)2156 .ok_or(Error::<T>::TokenNotFound)?;2157 Self::remove_token_index(collection_id, item_id, owner)?;21582159 2160 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2161 .checked_sub(rft_balance.fraction)2162 .ok_or(Error::<T>::NumOverflow)?;2163 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21642165 2166 let index = token2167 .owner2168 .iter()2169 .position(|i| i.owner == *owner)2170 .expect("owned item is exists");2171 token.owner.remove(index);2172 let owner_count = token.owner.len();21732174 2175 if owner_count == 0 {2176 <ReFungibleItemList<T>>::remove(collection_id, item_id);2177 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2178 }2179 else {2180 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2181 }21822183 Ok(())2184 }21852186 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2187 let collection_id = collection.id;21882189 let item = <NftItemList<T>>::get(collection_id, item_id)2190 .ok_or(Error::<T>::TokenNotFound)?;2191 Self::remove_token_index(collection_id, item_id, &item.owner)?;21922193 2194 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2195 .checked_sub(1)2196 .ok_or(Error::<T>::NumOverflow)?;2197 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2198 <NftItemList<T>>::remove(collection_id, item_id);2199 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);22002201 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2202 Ok(())2203 }22042205 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2206 let collection_id = collection.id;22072208 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2209 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);22102211 2212 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2213 .checked_sub(value)2214 .ok_or(Error::<T>::NumOverflow)?;2215 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22162217 if balance.value - value > 0 {2218 balance.value -= value;2219 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2220 }2221 else {2222 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2223 }22242225 collection.log(ERC20Events::Transfer {2226 from: *owner.as_eth(),2227 to: H160::default(),2228 value: value.into(),2229 });2230 Ok(())2231 }22322233 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2234 Ok(<CollectionHandle<T>>::get(collection_id)2235 .ok_or(Error::<T>::CollectionNotFound)?)2236 }22372238 fn save_collection(collection: CollectionHandle<T>) {2239 <CollectionById<T>>::insert(collection.id, collection.into_inner());2240 }22412242 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2243 if collection.logs.is_empty() {2244 return Ok(())2245 }2246 T::EthereumTransactionSender::submit_logs_transaction(2247 eth::generate_transaction(collection.id, T::EthereumChainId::get()),2248 collection.logs.retrieve_logs(),2249 )2250 }22512252 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2253 ensure!(2254 *subject == target_collection.owner,2255 Error::<T>::NoPermission2256 );22572258 Ok(())2259 }22602261 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2262 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2263 }22642265 fn check_owner_or_admin_permissions(2266 collection: &CollectionHandle<T>,2267 subject: &T::CrossAccountId,2268 ) -> DispatchResult {2269 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22702271 Ok(())2272 }22732274 fn owned_amount(2275 subject: &T::CrossAccountId,2276 target_collection: &CollectionHandle<T>,2277 item_id: TokenId,2278 ) -> Option<u128> {2279 let collection_id = target_collection.id;22802281 match target_collection.mode {2282 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2283 .then(|| 1),2284 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2285 .value),2286 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2287 .owner2288 .iter()2289 .find(|i| i.owner == *subject)2290 .map(|i| i.fraction),2291 CollectionMode::Invalid => None,2292 }2293 }22942295 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2296 match target_collection.mode {2297 CollectionMode::Fungible(_) => true,2298 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2299 }2300 }23012302 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2303 let collection_id = collection.id;23042305 let mes = Error::<T>::AddresNotInWhiteList;2306 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);23072308 Ok(())2309 }23102311 2312 2313 fn token_exists(2314 target_collection: &CollectionHandle<T>,2315 item_id: TokenId,2316 ) -> DispatchResult {2317 let collection_id = target_collection.id;2318 let exists = match target_collection.mode2319 {2320 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2321 CollectionMode::Fungible(_) => true,2322 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2323 _ => false2324 };23252326 ensure!(exists == true, Error::<T>::TokenNotFound);2327 Ok(())2328 }23292330 fn transfer_fungible(2331 collection: &CollectionHandle<T>,2332 value: u128,2333 owner: &T::CrossAccountId,2334 recipient: &T::CrossAccountId,2335 ) -> DispatchResult {2336 let collection_id = collection.id;23372338 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2339 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23402341 2342 Self::add_fungible_item(collection, recipient, value)?;23432344 2345 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23462347 2348 if balance.value == value {2349 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2350 }2351 else {2352 balance.value -= value;2353 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2354 }23552356 collection.log(ERC20Events::Transfer {2357 from: *owner.as_eth(),2358 to: *recipient.as_eth(),2359 value: value.into(),2360 });2361 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23622363 Ok(())2364 }23652366 fn transfer_refungible(2367 collection: &CollectionHandle<T>,2368 item_id: TokenId,2369 value: u128,2370 owner: T::CrossAccountId,2371 new_owner: T::CrossAccountId,2372 ) -> DispatchResult {2373 let collection_id = collection.id;2374 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2375 .ok_or(Error::<T>::TokenNotFound)?;23762377 let item = full_item2378 .owner2379 .iter()2380 .filter(|i| i.owner == owner)2381 .next()2382 .ok_or(Error::<T>::TokenNotFound)?;2383 let amount = item.fraction;23842385 ensure!(amount >= value, Error::<T>::TokenValueTooLow);23862387 2388 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2389 .checked_sub(value)2390 .ok_or(Error::<T>::NumOverflow)?;2391 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23922393 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2394 .checked_add(value)2395 .ok_or(Error::<T>::NumOverflow)?;2396 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23972398 let old_owner = item.owner.clone();2399 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);24002401 2402 if amount == value && !new_owner_has_account {2403 2404 2405 let mut new_full_item = full_item.clone();2406 new_full_item2407 .owner2408 .iter_mut()2409 .find(|i| i.owner == owner)2410 .expect("old owner does present in refungible")2411 .owner = new_owner.clone();2412 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24132414 2415 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2416 } else {2417 let mut new_full_item = full_item.clone();2418 new_full_item2419 .owner2420 .iter_mut()2421 .find(|i| i.owner == owner)2422 .expect("old owner does present in refungible")2423 .fraction -= value;24242425 2426 if new_owner_has_account {2427 2428 new_full_item2429 .owner2430 .iter_mut()2431 .find(|i| i.owner == new_owner)2432 .expect("new owner has account")2433 .fraction += value;2434 } else {2435 2436 new_full_item.owner.push(Ownership {2437 owner: new_owner.clone(),2438 fraction: value,2439 });2440 Self::add_token_index(collection_id, item_id, &new_owner)?;2441 }24422443 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2444 }24452446 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24472448 Ok(())2449 }24502451 fn transfer_nft(2452 collection: &CollectionHandle<T>,2453 item_id: TokenId,2454 sender: T::CrossAccountId,2455 new_owner: T::CrossAccountId,2456 ) -> DispatchResult {2457 let collection_id = collection.id;2458 let mut item = <NftItemList<T>>::get(collection_id, item_id)2459 .ok_or(Error::<T>::TokenNotFound)?;24602461 ensure!(2462 sender == item.owner,2463 Error::<T>::MustBeTokenOwner2464 );24652466 2467 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2468 .checked_sub(1)2469 .ok_or(Error::<T>::NumOverflow)?;2470 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24712472 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2473 .checked_add(1)2474 .ok_or(Error::<T>::NumOverflow)?;2475 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24762477 2478 let old_owner = item.owner.clone();2479 item.owner = new_owner.clone();2480 <NftItemList<T>>::insert(collection_id, item_id, item);24812482 2483 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24842485 collection.log(ERC721Events::Transfer {2486 from: *sender.as_eth(),2487 to: *new_owner.as_eth(),2488 token_id: item_id.into(),2489 });2490 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24912492 Ok(())2493 }2494 2495 fn set_re_fungible_variable_data(2496 collection: &CollectionHandle<T>,2497 item_id: TokenId,2498 data: Vec<u8>2499 ) -> DispatchResult {2500 let collection_id = collection.id;2501 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2502 .ok_or(Error::<T>::TokenNotFound)?;25032504 item.variable_data = data;25052506 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25072508 Ok(())2509 }25102511 fn set_nft_variable_data(2512 collection: &CollectionHandle<T>,2513 item_id: TokenId,2514 data: Vec<u8>2515 ) -> DispatchResult {2516 let collection_id = collection.id;2517 let mut item = <NftItemList<T>>::get(collection_id, item_id)2518 .ok_or(Error::<T>::TokenNotFound)?;2519 2520 item.variable_data = data;25212522 <NftItemList<T>>::insert(collection_id, item_id, item);2523 2524 Ok(())2525 }25262527 fn init_collection(item: &Collection<T>) {2528 2529 assert!(2530 item.decimal_points <= MAX_DECIMAL_POINTS,2531 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2532 );2533 assert!(2534 item.name.len() <= 64,2535 "Collection name can not be longer than 63 char"2536 );2537 assert!(2538 item.name.len() <= 256,2539 "Collection description can not be longer than 255 char"2540 );2541 assert!(2542 item.token_prefix.len() <= 16,2543 "Token prefix can not be longer than 15 char"2544 );25452546 2547 let next_id = CreatedCollectionCount::get()2548 .checked_add(1)2549 .unwrap();25502551 CreatedCollectionCount::put(next_id);2552 }25532554 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2555 let current_index = <ItemListIndex>::get(collection_id)2556 .checked_add(1)2557 .unwrap();25582559 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25602561 <ItemListIndex>::insert(collection_id, current_index);25622563 2564 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2565 .checked_add(1)2566 .unwrap();2567 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2568 }25692570 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2571 let current_index = <ItemListIndex>::get(collection_id)2572 .checked_add(1)2573 .unwrap();25742575 Self::add_token_index(collection_id, current_index, owner).unwrap();25762577 <ItemListIndex>::insert(collection_id, current_index);25782579 2580 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2581 .checked_add(item.value)2582 .unwrap();2583 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2584 }25852586 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2587 let current_index = <ItemListIndex>::get(collection_id)2588 .checked_add(1)2589 .unwrap();25902591 let value = item.owner.first().unwrap().fraction;2592 let owner = item.owner.first().unwrap().owner.clone();25932594 Self::add_token_index(collection_id, current_index, &owner).unwrap();25952596 <ItemListIndex>::insert(collection_id, current_index);25972598 2599 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2600 .checked_add(value)2601 .unwrap();2602 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2603 }26042605 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2606 2607 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26082609 2610 let count = <AccountItemCount<T>>::get(owner.as_sub());2611 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26122613 <AccountItemCount<T>>::insert(owner.as_sub(), count2614 .checked_add(1)2615 .ok_or(Error::<T>::NumOverflow)?);2616 }2617 else {2618 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2619 }26202621 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2622 if list_exists {2623 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2624 let item_contains = list.contains(&item_index.clone());26252626 if !item_contains {2627 list.push(item_index.clone());2628 }26292630 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2631 } else {2632 let mut itm = Vec::new();2633 itm.push(item_index.clone());2634 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2635 }26362637 Ok(())2638 }26392640 fn remove_token_index(2641 collection_id: CollectionId,2642 item_index: TokenId,2643 owner: &T::CrossAccountId,2644 ) -> DispatchResult {26452646 2647 <AccountItemCount<T>>::insert(owner.as_sub(), 2648 <AccountItemCount<T>>::get(owner.as_sub())2649 .checked_sub(1)2650 .ok_or(Error::<T>::NumOverflow)?);265126522653 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2654 if list_exists {2655 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2656 let item_contains = list.contains(&item_index.clone());26572658 if item_contains {2659 list.retain(|&item| item != item_index);2660 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2661 }2662 }26632664 Ok(())2665 }26662667 fn move_token_index(2668 collection_id: CollectionId,2669 item_index: TokenId,2670 old_owner: &T::CrossAccountId,2671 new_owner: &T::CrossAccountId,2672 ) -> DispatchResult {2673 Self::remove_token_index(collection_id, item_index, old_owner)?;2674 Self::add_token_index(collection_id, item_index, new_owner)?;26752676 Ok(())2677 }2678 2679 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2680 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26812682 Ok(())2683 }2684}2685268626872688268926902691pub type Multiplier = FixedU128;26922693type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;2694269526962697#[derive(Encode, Decode, Clone, Eq, PartialEq)]2698pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26992700impl<T: Config + Send + Sync> sp_std::fmt::Debug 2701 for ChargeTransactionPayment<T>2702{2703 #[cfg(feature = "std")]2704 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2705 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2706 }2707 #[cfg(not(feature = "std"))]2708 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2709 Ok(())2710 }2711}27122713impl<T: Config> ChargeTransactionPayment<T>2714where2715 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2716 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2717 T::AccountId: AsRef<[u8]>,2718 T::AccountId: UncheckedFrom<T::Hash>,2719{2720 fn traditional_fee(2721 len: usize,2722 info: &DispatchInfoOf<T::Call>,2723 tip: BalanceOf<T>,2724 ) -> BalanceOf<T>2725 where2726 T::Call: Dispatchable<Info = DispatchInfo>,2727 {2728 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2729 }27302731 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2732 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2733 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2734 let len_saturation = max_block_length as u64 / (len as u64).max(1);2735 let coefficient: BalanceOf<T> = weight_saturation2736 .min(len_saturation)2737 .saturated_into::<BalanceOf<T>>();2738 final_fee2739 .saturating_mul(coefficient)2740 .saturated_into::<TransactionPriority>()2741 }27422743 fn withdraw_fee(2744 &self,2745 who: &T::AccountId,2746 call: &T::Call,2747 info: &DispatchInfoOf<T::Call>,2748 len: usize,2749 ) -> Result<2750 (2751 BalanceOf<T>,2752 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2753 ),2754 TransactionValidityError,2755 > {2756 let tip = self.0;27572758 let fee = Self::traditional_fee(len, info, tip);27592760 2761 if fee.is_zero() {2762 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2763 .map(|i| (fee, i));2764 }27652766 2767 2768 let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2769 Some(Call::create_item(collection_id, _owner, _properties)) => {2770 let collection = <CollectionById<T>>::get(collection_id)?;27712772 2773 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27742775 let limit = collection.limits.sponsor_transfer_timeout;2776 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2777 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2778 let limit_time = last_tx_block + limit.into();2779 if block_number <= limit_time {2780 return None;2781 }2782 }2783 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27842785 2786 if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2787 collection.sponsorship.sponsor()2788 .cloned()2789 } else {2790 None2791 }2792 }2793 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2794 let collection = <CollectionById<T>>::get(collection_id)?;2795 2796 let mut sponsor_transfer = false;2797 if collection.sponsorship.confirmed() {27982799 let collection_limits = collection.limits;2800 let collection_mode = collection.mode;2801 2802 2803 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2804 sponsor_transfer = match collection_mode {2805 CollectionMode::NFT => {2806 2807 2808 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2809 collection_limits.sponsor_transfer_timeout2810 } else {2811 ChainLimit::get().nft_sponsor_transfer_timeout2812 };2813 2814 let mut sponsored = true;2815 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2816 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2817 let limit_time = last_tx_block + limit.into();2818 if block_number <= limit_time {2819 sponsored = false;2820 }2821 }2822 if sponsored {2823 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2824 }28252826 sponsored2827 }2828 CollectionMode::Fungible(_) => {2829 2830 2831 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2832 collection_limits.sponsor_transfer_timeout2833 } else {2834 ChainLimit::get().fungible_sponsor_transfer_timeout2835 };2836 2837 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2838 let mut sponsored = true;2839 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2840 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2841 let limit_time = last_tx_block + limit.into();2842 if block_number <= limit_time {2843 sponsored = false;2844 }2845 }2846 if sponsored {2847 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2848 }28492850 sponsored2851 }2852 CollectionMode::ReFungible => {2853 2854 2855 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2856 collection_limits.sponsor_transfer_timeout2857 } else {2858 ChainLimit::get().refungible_sponsor_transfer_timeout2859 };2860 2861 let mut sponsored = true;2862 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2863 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2864 let limit_time = last_tx_block + limit.into();2865 if block_number <= limit_time {2866 sponsored = false;2867 }2868 }2869 if sponsored {2870 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2871 }28722873 sponsored2874 }2875 _ => {2876 false2877 },2878 };2879 }28802881 if !sponsor_transfer {2882 None2883 } else {2884 collection.sponsorship.sponsor()2885 .cloned()2886 }2887 }28882889 Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2890 let mut sponsor_metadata_changes = false;28912892 let collection = <CollectionById<T>>::get(collection_id)?;28932894 if2895 collection.sponsorship.confirmed() &&2896 2897 2898 !matches!(collection.mode, CollectionMode::Fungible(_)) &&2899 data.len() <= collection.limits.sponsored_data_size as usize2900 {2901 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2902 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29032904 if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2905 .map(|last_block| block_number - last_block > rate_limit)2906 .unwrap_or(true) 2907 {2908 sponsor_metadata_changes = true;2909 <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2910 }2911 }2912 }29132914 if !sponsor_metadata_changes {2915 None2916 } else {2917 collection.sponsorship.sponsor().cloned()2918 }2919 }29202921 _ => None,2922 })();29232924 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2925 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29262927 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29282929 let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2930 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2931 2932 if !owned_contract && white_list_enabled {2933 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2934 return Err(InvalidTransaction::Call.into());2935 }2936 }2937 },2938 _ => {},2939 }29402941 2942 sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29432944 2945 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29462947 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2948 &who,2949 code_hash,2950 salt,2951 );2952 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29532954 None2955 },29562957 2958 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {29592960 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2961 &who,2962 &T::Hashing::hash(&_code),2963 _salt,2964 );29652966 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29672968 None2969 }29702971 2972 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29732974 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29752976 let mut sponsor_transfer = false;2977 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2978 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2979 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2980 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2981 let limit_time = last_tx_block + rate_limit;29822983 if block_number >= limit_time {2984 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2985 sponsor_transfer = true;2986 }2987 } else {2988 sponsor_transfer = false;2989 }2990 2991 if sponsor_transfer {2992 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2993 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2994 return Some(called_contract);2995 }2996 }2997 }29982999 None3000 },30013002 _ => None,3003 });30043005 let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30063007 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3008 .map(|i| (fee, i))3009 }3010}301130123013impl<T: Config + Send + Sync> SignedExtension3014 for ChargeTransactionPayment<T>3015where3016 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3017 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3018 T::AccountId: AsRef<[u8]>,3019 T::AccountId: UncheckedFrom<T::Hash>,3020{3021 const IDENTIFIER: &'static str = "ChargeTransactionPayment";3022 type AccountId = T::AccountId;3023 type Call = T::Call;3024 type AdditionalSigned = ();3025 type Pre = (3026 3027 BalanceOf<T>,3028 3029 Self::AccountId,3030 3031 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3032 );3033 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3034 Ok(())3035 }30363037 fn validate(3038 &self,3039 who: &Self::AccountId,3040 call: &Self::Call,3041 info: &DispatchInfoOf<Self::Call>,3042 len: usize,3043 ) -> TransactionValidity {3044 let (fee, _) = self.withdraw_fee(who, call, info, len)?;3045 Ok(ValidTransaction {3046 priority: Self::get_priority(len, info, fee),3047 ..Default::default()3048 })3049 }30503051 fn pre_dispatch(3052 self,3053 who: &Self::AccountId,3054 call: &Self::Call,3055 info: &DispatchInfoOf<Self::Call>,3056 len: usize,3057 ) -> Result<Self::Pre, TransactionValidityError> {3058 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3059 Ok((self.0, who.clone(), imbalance))3060 }30613062 fn post_dispatch(3063 pre: Self::Pre,3064 info: &DispatchInfoOf<Self::Call>,3065 post_info: &PostDispatchInfoOf<Self::Call>,3066 len: usize,3067 _result: &DispatchResult,3068 ) -> Result<(), TransactionValidityError> {3069 let (tip, who, imbalance) = pre;3070 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3071 len as u32,3072 info,3073 post_info,3074 tip,3075 );3076 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3077 Ok(())3078 }3079}3080308130823083sp_api::decl_runtime_apis! {3084 pub trait NftApi {3085 3086 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3087 }3088}