difftreelog
refactor review unwraps
in: master
1 file changed
pallets/nft/src/lib.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![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 transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_runtime::sp_std::prelude::Vec;36use sp_runtime::{37 traits::{38 Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,39 },40 transaction_validity::{41 TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,42 },43 FixedPointOperand, FixedU128,44};45use sp_runtime::traits::StaticLookup;46use pallet_contracts::chain_extension::UncheckedFrom;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;5657pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;58pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;59pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;60pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6162// Structs63// #region6465pub type CollectionId = u32;66pub type TokenId = u32;67pub type DecimalPoints = u8;6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum CollectionMode {72 Invalid,73 NFT,74 // decimal points75 Fungible(DecimalPoints),76 ReFungible,77}7879impl Default for CollectionMode {80 fn default() -> Self {81 Self::Invalid82 }83}8485impl Into<u8> for CollectionMode {86 fn into(self) -> u8 {87 match self {88 CollectionMode::Invalid => 0,89 CollectionMode::NFT => 1,90 CollectionMode::Fungible(_) => 2,91 CollectionMode::ReFungible => 3,92 }93 }94}9596#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]97#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]98pub enum AccessMode {99 Normal,100 WhiteList,101}102impl Default for AccessMode {103 fn default() -> Self {104 Self::Normal105 }106}107108#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]109#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]110pub enum SchemaVersion {111 ImageURL,112 Unique,113}114impl Default for SchemaVersion {115 fn default() -> Self {116 Self::ImageURL117 }118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct Ownership<AccountId> {123 pub owner: AccountId,124 pub fraction: u128,125}126127#[derive(Encode, Decode, Debug, Clone, PartialEq)]128#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]129pub enum SponsorshipState<AccountId> {130 /// The fees are applied to the transaction sender131 Disabled,132 Unconfirmed(AccountId),133 /// Transactions are sponsored by specified account134 Confirmed(AccountId),135}136137impl<AccountId> SponsorshipState<AccountId> {138 fn sponsor(&self) -> Option<&AccountId> {139 match self {140 Self::Confirmed(sponsor) => Some(sponsor),141 _ => None,142 }143 }144145 fn pending_sponsor(&self) -> Option<&AccountId> {146 match self {147 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),148 _ => None,149 }150 }151152 fn confirmed(&self) -> bool {153 matches!(self, Self::Confirmed(_))154 }155}156157impl<T> Default for SponsorshipState<T> {158 fn default() -> Self {159 Self::Disabled160 }161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct CollectionType<AccountId> {166 pub owner: AccountId,167 pub mode: CollectionMode,168 pub access: AccessMode,169 pub decimal_points: DecimalPoints,170 pub name: Vec<u16>, // 64 include null escape char171 pub description: Vec<u16>, // 256 include null escape char172 pub token_prefix: Vec<u8>, // 16 include null escape char173 pub mint_mode: bool,174 pub offchain_schema: Vec<u8>,175 pub schema_version: SchemaVersion,176 pub sponsorship: SponsorshipState<AccountId>,177 pub limits: CollectionLimits, // Collection private restrictions 178 pub variable_on_chain_schema: Vec<u8>, //179 pub const_on_chain_schema: Vec<u8>, //180}181182#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]183#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]184pub struct NftItemType<AccountId> {185 pub owner: AccountId,186 pub const_data: Vec<u8>,187 pub variable_data: Vec<u8>,188}189190#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]191#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]192pub struct FungibleItemType {193 pub value: u128,194}195196#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct ReFungibleItemType<AccountId> {199 pub owner: Vec<Ownership<AccountId>>,200 pub const_data: Vec<u8>,201 pub variable_data: Vec<u8>,202}203204// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]205// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206// pub struct VestingItem<AccountId, Moment> {207// pub sender: AccountId,208// pub recipient: AccountId,209// pub collection_id: CollectionId,210// pub item_id: TokenId,211// pub amount: u64,212// pub vesting_date: Moment,213// }214215#[derive(Encode, Decode, Debug, Clone, PartialEq)]216#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]217pub struct CollectionLimits {218 pub account_token_ownership_limit: u32,219 pub sponsored_data_size: u32,220 pub token_limit: u32,221222 // Timeouts for item types in passed blocks223 pub sponsor_transfer_timeout: u32,224 pub owner_can_transfer: bool,225 pub owner_can_destroy: bool,226}227228impl Default for CollectionLimits {229 fn default() -> CollectionLimits {230 CollectionLimits { 231 account_token_ownership_limit: 10_000_000, 232 token_limit: u32::max_value(),233 sponsored_data_size: u32::MAX,234 sponsor_transfer_timeout: 14400,235 owner_can_transfer: true,236 owner_can_destroy: true237 }238 }239}240241#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]242#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]243pub struct ChainLimits {244 pub collection_numbers_limit: u32,245 pub account_token_ownership_limit: u32,246 pub collections_admins_limit: u64,247 pub custom_data_limit: u32,248249 // Timeouts for item types in passed blocks250 pub nft_sponsor_transfer_timeout: u32,251 pub fungible_sponsor_transfer_timeout: u32,252 pub refungible_sponsor_transfer_timeout: u32,253254 // Schema limits255 pub offchain_schema_limit: u32,256 pub variable_on_chain_schema_limit: u32,257 pub const_on_chain_schema_limit: u32,258}259260pub trait WeightInfo {261 fn create_collection() -> Weight;262 fn destroy_collection() -> Weight;263 fn add_to_white_list() -> Weight;264 fn remove_from_white_list() -> Weight;265 fn set_public_access_mode() -> Weight;266 fn set_mint_permission() -> Weight;267 fn change_collection_owner() -> Weight;268 fn add_collection_admin() -> Weight;269 fn remove_collection_admin() -> Weight;270 fn set_collection_sponsor() -> Weight;271 fn confirm_sponsorship() -> Weight;272 fn remove_collection_sponsor() -> Weight;273 fn create_item(s: usize) -> Weight;274 fn burn_item() -> Weight;275 fn transfer() -> Weight;276 fn approve() -> Weight;277 fn transfer_from() -> Weight;278 fn set_offchain_schema() -> Weight;279 fn set_const_on_chain_schema() -> Weight;280 fn set_variable_on_chain_schema() -> Weight;281 fn set_variable_meta_data() -> Weight;282 fn enable_contract_sponsoring() -> Weight;283 fn set_schema_version() -> Weight;284 fn set_chain_limits() -> Weight;285 fn set_contract_sponsoring_rate_limit() -> Weight;286 fn toggle_contract_white_list() -> Weight;287 fn add_to_contract_white_list() -> Weight;288 fn remove_from_contract_white_list() -> Weight;289 fn set_collection_limits() -> Weight;290}291292#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]293#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]294pub struct CreateNftData {295 pub const_data: Vec<u8>,296 pub variable_data: Vec<u8>,297}298299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]300#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]301pub struct CreateFungibleData {302 pub value: u128,303}304305#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]306#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]307pub struct CreateReFungibleData {308 pub const_data: Vec<u8>,309 pub variable_data: Vec<u8>,310 pub pieces: u128,311}312313#[derive(Encode, Decode, Debug, Clone, PartialEq)]314#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]315pub enum CreateItemData {316 NFT(CreateNftData),317 Fungible(CreateFungibleData),318 ReFungible(CreateReFungibleData),319}320321impl CreateItemData {322 pub fn len(&self) -> usize {323 let len = match self {324 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),325 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),326 _ => 0327 };328 329 return len;330 }331}332333impl From<CreateNftData> for CreateItemData {334 fn from(item: CreateNftData) -> Self {335 CreateItemData::NFT(item)336 }337}338339impl From<CreateReFungibleData> for CreateItemData {340 fn from(item: CreateReFungibleData) -> Self {341 CreateItemData::ReFungible(item)342 }343}344345impl From<CreateFungibleData> for CreateItemData {346 fn from(item: CreateFungibleData) -> Self {347 CreateItemData::Fungible(item)348 }349}350351352decl_error! {353 /// Error for non-fungible-token module.354 pub enum Error for Module<T: Config> {355 /// Total collections bound exceeded.356 TotalCollectionsLimitExceeded,357 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.358 CollectionDecimalPointLimitExceeded, 359 /// Collection name can not be longer than 63 char.360 CollectionNameLimitExceeded, 361 /// Collection description can not be longer than 255 char.362 CollectionDescriptionLimitExceeded, 363 /// Token prefix can not be longer than 15 char.364 CollectionTokenPrefixLimitExceeded,365 /// This collection does not exist.366 CollectionNotFound,367 /// Item not exists.368 TokenNotFound,369 /// Admin not found370 AdminNotFound,371 /// Arithmetic calculation overflow.372 NumOverflow, 373 /// Account already has admin role.374 AlreadyAdmin, 375 /// You do not own this collection.376 NoPermission,377 /// This address is not set as sponsor, use setCollectionSponsor first.378 ConfirmUnsetSponsorFail,379 /// Collection is not in mint mode.380 PublicMintingNotAllowed,381 /// Sender parameter and item owner must be equal.382 MustBeTokenOwner,383 /// Item balance not enough.384 TokenValueTooLow,385 /// Size of item is too large.386 NftSizeLimitExceeded,387 /// No approve found388 ApproveNotFound,389 /// Requested value more than approved.390 TokenValueNotEnough,391 /// Only approved addresses can call this method.392 ApproveRequired,393 /// Address is not in white list.394 AddresNotInWhiteList,395 /// Number of collection admins bound exceeded.396 CollectionAdminsLimitExceeded,397 /// Owned tokens by a single address bound exceeded.398 AddressOwnershipLimitExceeded,399 /// Length of items properties must be greater than 0.400 EmptyArgument,401 /// const_data exceeded data limit.402 TokenConstDataLimitExceeded,403 /// variable_data exceeded data limit.404 TokenVariableDataLimitExceeded,405 /// Not NFT item data used to mint in NFT collection.406 NotNftDataUsedToMintNftCollectionToken,407 /// Not Fungible item data used to mint in Fungible collection.408 NotFungibleDataUsedToMintFungibleCollectionToken,409 /// Not Re Fungible item data used to mint in Re Fungible collection.410 NotReFungibleDataUsedToMintReFungibleCollectionToken,411 /// Unexpected collection type.412 UnexpectedCollectionType,413 /// Can't store metadata in fungible tokens.414 CantStoreMetadataInFungibleTokens,415 /// Collection token limit exceeded416 CollectionTokenLimitExceeded,417 /// Account token limit exceeded per collection418 AccountTokenLimitExceeded,419 /// Collection limit bounds per collection exceeded420 CollectionLimitBoundsExceeded,421 /// Tried to enable permissions which are only permitted to be disabled422 OwnerPermissionsCantBeReverted,423 /// Schema data size limit bound exceeded424 SchemaDataLimitExceeded,425 /// Maximum refungibility exceeded426 WrongRefungiblePieces427 }428}429430pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {431 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;432433 /// Weight information for extrinsics in this pallet.434 type WeightInfo: WeightInfo;435}436437#[cfg(feature = "runtime-benchmarks")]438mod benchmarking;439440// #endregion441442// # Used definitions443//444// ## User control levels445//446// chain-controlled - key is uncontrolled by user447// i.e autoincrementing index448// can use non-cryptographic hash449// real - key is controlled by user450// but it is hard to generate enough colliding values, i.e owner of signed txs451// can use non-cryptographic hash452// controlled - key is completly controlled by users453// i.e maps with mutable keys454// should use cryptographic hash455//456// ## User control level downgrade reasons457//458// ?1 - chain-controlled -> controlled459// collections/tokens can be destroyed, resulting in massive holes460// ?2 - chain-controlled -> controlled461// same as ?1, but can be only added, resulting in easier exploitation462// ?3 - real -> controlled463// no confirmation required, so addresses can be easily generated464decl_storage! {465 trait Store for Module<T: Config> as Nft {466467 //#region Private members468 /// Id of next collection469 CreatedCollectionCount: u32;470 /// Used for migrations471 ChainVersion: u64;472 /// Id of last collection token473 /// Collection id (controlled?1)474 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;475 //#endregion476477 //#region Chain limits struct478 pub ChainLimit get(fn chain_limit) config(): ChainLimits;479 //#endregion480481 //#region Bound counters482 /// Amount of collections destroyed, used for total amount tracking with483 /// CreatedCollectionCount484 DestroyedCollectionCount: u32;485 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)486 /// Account id (real)487 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;488 //#endregion489490 //#region Basic collections491 /// Collection info492 /// Collection id (controlled?1)493 pub Collection get(fn collection) config(): map hasher(blake2_128_concat) CollectionId => CollectionType<T::AccountId>;494 /// List of collection admins495 /// Collection id (controlled?2)496 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;497 /// Whitelisted collection users498 /// Collection id (controlled?2), user id (controlled?3)499 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;500 //#endregion501502 /// How many of collection items user have503 /// Collection id (controlled?2), account id (real)504 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;505506 /// Amount of items which spender can transfer out of owners account (via transferFrom)507 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))508 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;509510 //#region Item collections511 /// Collection id (controlled?2), token id (controlled?1)512 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;513 /// Collection id (controlled?2), owner (controlled?2)514 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;515 /// Collection id (controlled?2), token id (controlled?1)516 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;517 //#endregion518519 //#region Index list520 /// Collection id (controlled?2), tokens owner (controlled?2)521 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;522 //#endregion523524 //#region Tokens transfer rate limit baskets525 /// (Collection id (controlled?2), who created (real))526 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;527 /// Collection id (controlled?2), token id (controlled?2)528 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;529 /// Collection id (controlled?2), owning user (real)530 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;531 /// Collection id (controlled?2), token id (controlled?2)532 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;533 //#endregion534535 //#region Contract Sponsorship and Ownership536 /// Contract address (real)537 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;538 /// Contract address (real)539 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;540 /// (Contract address(real), caller (real))541 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;542 /// Contract address (real)543 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;544 /// Contract address (real)545 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 546 /// Contract address (real) => Whitelisted user (controlled?3)547 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 548 //#endregion549 }550 add_extra_genesis {551 build(|config: &GenesisConfig<T>| {552 // Modification of storage553 for (_num, _c) in &config.collection {554 <Module<T>>::init_collection(_c);555 }556557 for (_num, _c, _i) in &config.nft_item_id {558 <Module<T>>::init_nft_token(*_c, _i);559 }560561 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {562 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);563 }564565 for (_num, _c, _i) in &config.refungible_item_id {566 <Module<T>>::init_refungible_token(*_c, _i);567 }568 })569 }570}571572decl_event!(573 pub enum Event<T>574 where575 AccountId = <T as system::Config>::AccountId,576 {577 /// New collection was created578 /// 579 /// # Arguments580 /// 581 /// * collection_id: Globally unique identifier of newly created collection.582 /// 583 /// * mode: [CollectionMode] converted into u8.584 /// 585 /// * account_id: Collection owner.586 Created(CollectionId, u8, AccountId),587588 /// New item was created.589 /// 590 /// # Arguments591 /// 592 /// * collection_id: Id of the collection where item was created.593 /// 594 /// * item_id: Id of an item. Unique within the collection.595 ///596 /// * recipient: Owner of newly created item 597 ItemCreated(CollectionId, TokenId, AccountId),598599 /// Collection item was burned.600 /// 601 /// # Arguments602 /// 603 /// collection_id.604 /// 605 /// item_id: Identifier of burned NFT.606 ItemDestroyed(CollectionId, TokenId),607608 /// Item was transferred609 ///610 /// * collection_id: Id of collection to which item is belong611 ///612 /// * item_id: Id of an item613 ///614 /// * sender: Original owner of item615 ///616 /// * recipient: New owner of item617 ///618 /// * amount: Always 1 for NFT619 Transfer(CollectionId, TokenId, AccountId, AccountId, u128),620 }621);622623decl_module! {624 pub struct Module<T: Config> for enum Call 625 where 626 origin: T::Origin627 {628 fn deposit_event() = default;629 type Error = Error<T>;630631 fn on_initialize(now: T::BlockNumber) -> Weight {632 0633 }634635 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.636 /// 637 /// # Permissions638 /// 639 /// * Anyone.640 /// 641 /// # Arguments642 /// 643 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.644 /// 645 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.646 /// 647 /// * token_prefix: UTF-8 string with token prefix.648 /// 649 /// * mode: [CollectionMode] collection type and type dependent data.650 // returns collection ID651 #[weight = <T as Config>::WeightInfo::create_collection()]652 #[transactional]653 pub fn create_collection(origin,654 collection_name: Vec<u16>,655 collection_description: Vec<u16>,656 token_prefix: Vec<u8>,657 mode: CollectionMode) -> DispatchResult {658659 // Anyone can create a collection660 let who = ensure_signed(origin)?;661662 let decimal_points = match mode {663 CollectionMode::Fungible(points) => points,664 _ => 0665 };666667 let chain_limit = ChainLimit::get();668669 let created_count = CreatedCollectionCount::get();670 let destroyed_count = DestroyedCollectionCount::get();671672 // bound Total number of collections673 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);674675 // check params676 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);677 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);678 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);679 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);680681 // Generate next collection ID682 let next_id = created_count683 .checked_add(1)684 .ok_or(Error::<T>::NumOverflow)?;685686 CreatedCollectionCount::put(next_id);687688 let limits = CollectionLimits {689 sponsored_data_size: chain_limit.custom_data_limit,690 ..Default::default()691 };692693 // Create new collection694 let new_collection = CollectionType {695 owner: who.clone(),696 name: collection_name,697 mode: mode.clone(),698 mint_mode: false,699 access: AccessMode::Normal,700 description: collection_description,701 decimal_points: decimal_points,702 token_prefix: token_prefix,703 offchain_schema: Vec::new(),704 schema_version: SchemaVersion::ImageURL,705 sponsorship: SponsorshipState::Disabled,706 variable_on_chain_schema: Vec::new(),707 const_on_chain_schema: Vec::new(),708 limits,709 };710711 // Add new collection to map712 <Collection<T>>::insert(next_id, new_collection);713714 // call event715 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));716717 Ok(())718 }719720 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.721 /// 722 /// # Permissions723 /// 724 /// * Collection Owner.725 /// 726 /// # Arguments727 /// 728 /// * collection_id: collection to destroy.729 #[weight = <T as Config>::WeightInfo::destroy_collection()]730 #[transactional]731 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {732733 let sender = ensure_signed(origin)?;734 Self::check_owner_permissions(collection_id, sender)?;735736 let target_collection = <Collection<T>>::get(collection_id);737 if !target_collection.limits.owner_can_destroy {738 fail!(Error::<T>::NoPermission);739 }740741 <AddressTokens<T>>::remove_prefix(collection_id);742 <Allowances<T>>::remove_prefix(collection_id);743 <Balance<T>>::remove_prefix(collection_id);744 <ItemListIndex>::remove(collection_id);745 <AdminList<T>>::remove(collection_id);746 <Collection<T>>::remove(collection_id);747 <WhiteList<T>>::remove_prefix(collection_id);748749 <NftItemList<T>>::remove_prefix(collection_id);750 <FungibleItemList<T>>::remove_prefix(collection_id);751 <ReFungibleItemList<T>>::remove_prefix(collection_id);752753 <NftTransferBasket<T>>::remove_prefix(collection_id);754 <FungibleTransferBasket<T>>::remove_prefix(collection_id);755 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);756757 DestroyedCollectionCount::put(DestroyedCollectionCount::get()758 .checked_add(1)759 .ok_or(Error::<T>::NumOverflow)?);760761 Ok(())762 }763764 /// Add an address to white list.765 /// 766 /// # Permissions767 /// 768 /// * Collection Owner769 /// * Collection Admin770 /// 771 /// # Arguments772 /// 773 /// * collection_id.774 /// 775 /// * address.776 #[weight = <T as Config>::WeightInfo::add_to_white_list()]777 #[transactional]778 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{779780 let sender = ensure_signed(origin)?;781 Self::check_owner_or_admin_permissions(collection_id, sender)?;782783 <WhiteList<T>>::insert(collection_id, address, true);784 785 Ok(())786 }787788 /// Remove an address from white list.789 /// 790 /// # Permissions791 /// 792 /// * Collection Owner793 /// * Collection Admin794 /// 795 /// # Arguments796 /// 797 /// * collection_id.798 /// 799 /// * address.800 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]801 #[transactional]802 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{803804 let sender = ensure_signed(origin)?;805 Self::check_owner_or_admin_permissions(collection_id, sender)?;806807 <WhiteList<T>>::remove(collection_id, address);808809 Ok(())810 }811812 /// Toggle between normal and white list access for the methods with access for `Anyone`.813 /// 814 /// # Permissions815 /// 816 /// * Collection Owner.817 /// 818 /// # Arguments819 /// 820 /// * collection_id.821 /// 822 /// * mode: [AccessMode]823 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]824 #[transactional]825 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult826 {827 let sender = ensure_signed(origin)?;828829 Self::check_owner_permissions(collection_id, sender)?;830 let mut target_collection = <Collection<T>>::get(collection_id);831 target_collection.access = mode;832 <Collection<T>>::insert(collection_id, target_collection);833834 Ok(())835 }836837 /// Allows Anyone to create tokens if:838 /// * White List is enabled, and839 /// * Address is added to white list, and840 /// * This method was called with True parameter841 /// 842 /// # Permissions843 /// * Collection Owner844 ///845 /// # Arguments846 /// 847 /// * collection_id.848 /// 849 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.850 #[weight = <T as Config>::WeightInfo::set_mint_permission()]851 #[transactional]852 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult853 {854 let sender = ensure_signed(origin)?;855856 Self::check_owner_permissions(collection_id, sender)?;857 let mut target_collection = <Collection<T>>::get(collection_id);858 target_collection.mint_mode = mint_permission;859 <Collection<T>>::insert(collection_id, target_collection);860861 Ok(())862 }863864 /// Change the owner of the collection.865 /// 866 /// # Permissions867 /// 868 /// * Collection Owner.869 /// 870 /// # Arguments871 /// 872 /// * collection_id.873 /// 874 /// * new_owner.875 #[weight = <T as Config>::WeightInfo::change_collection_owner()]876 #[transactional]877 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {878879 let sender = ensure_signed(origin)?;880 Self::check_owner_permissions(collection_id, sender)?;881 let mut target_collection = <Collection<T>>::get(collection_id);882 target_collection.owner = new_owner;883 <Collection<T>>::insert(collection_id, target_collection);884885 Ok(())886 }887888 /// Adds an admin of the Collection.889 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 890 /// 891 /// # Permissions892 /// 893 /// * Collection Owner.894 /// * Collection Admin.895 /// 896 /// # Arguments897 /// 898 /// * collection_id: ID of the Collection to add admin for.899 /// 900 /// * new_admin_id: Address of new admin to add.901 #[weight = <T as Config>::WeightInfo::add_collection_admin()]902 #[transactional]903 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {904905 let sender = ensure_signed(origin)?;906 Self::check_owner_or_admin_permissions(collection_id, sender)?;907 let mut admin_arr: Vec<T::AccountId> = Vec::new();908909 if <AdminList<T>>::contains_key(collection_id)910 {911 admin_arr = <AdminList<T>>::get(collection_id);912 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);913 }914915 // Number of collection admins916 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);917918 admin_arr.push(new_admin_id);919 <AdminList<T>>::insert(collection_id, admin_arr);920921 Ok(())922 }923924 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.925 ///926 /// # Permissions927 /// 928 /// * Collection Owner.929 /// * Collection Admin.930 /// 931 /// # Arguments932 /// 933 /// * collection_id: ID of the Collection to remove admin for.934 /// 935 /// * account_id: Address of admin to remove.936 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]937 #[transactional]938 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {939940 let sender = ensure_signed(origin)?;941 Self::check_owner_or_admin_permissions(collection_id, sender)?;942 ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);943944 let mut admin_arr = <AdminList<T>>::get(collection_id);945 admin_arr.retain(|i| *i != account_id);946 <AdminList<T>>::insert(collection_id, admin_arr);947948 Ok(())949 }950951 /// # Permissions952 /// 953 /// * Collection Owner954 /// 955 /// # Arguments956 /// 957 /// * collection_id.958 /// 959 /// * new_sponsor.960 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]961 #[transactional]962 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {963964 let sender = ensure_signed(origin)?;965 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);966967 let mut target_collection = <Collection<T>>::get(collection_id);968 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);969970 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);971 <Collection<T>>::insert(collection_id, target_collection);972973 Ok(())974 }975976 /// # Permissions977 /// 978 /// * Sponsor.979 /// 980 /// # Arguments981 /// 982 /// * collection_id.983 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]984 #[transactional]985 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {986987 let sender = ensure_signed(origin)?;988 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);989990 let mut target_collection = <Collection<T>>::get(collection_id);991 ensure!(992 target_collection.sponsorship.pending_sponsor() == Some(&sender),993 Error::<T>::ConfirmUnsetSponsorFail994 );995996 target_collection.sponsorship = SponsorshipState::Confirmed(sender);997 <Collection<T>>::insert(collection_id, target_collection);998999 Ok(())1000 }10011002 /// Switch back to pay-per-own-transaction model.1003 ///1004 /// # Permissions1005 ///1006 /// * Collection owner.1007 /// 1008 /// # Arguments1009 /// 1010 /// * collection_id.1011 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1012 #[transactional]1013 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10141015 let sender = ensure_signed(origin)?;1016 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);10171018 let mut target_collection = <Collection<T>>::get(collection_id);1019 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);10201021 target_collection.sponsorship = SponsorshipState::Disabled;1022 <Collection<T>>::insert(collection_id, target_collection);10231024 Ok(())1025 }10261027 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1028 /// 1029 /// # Permissions1030 /// 1031 /// * Collection Owner.1032 /// * Collection Admin.1033 /// * Anyone if1034 /// * White List is enabled, and1035 /// * Address is added to white list, and1036 /// * MintPermission is enabled (see SetMintPermission method)1037 /// 1038 /// # Arguments1039 /// 1040 /// * collection_id: ID of the collection.1041 /// 1042 /// * owner: Address, initial owner of the NFT.1043 ///1044 /// * data: Token data to store on chain.1045 // #[weight =1046 // (130_000_000 as Weight)1047 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1048 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1049 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]10501051 #[weight = <T as Config>::WeightInfo::create_item(data.len())]1052 #[transactional]1053 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {10541055 let sender = ensure_signed(origin)?;10561057 Self::collection_exists(collection_id)?;10581059 let target_collection = <Collection<T>>::get(collection_id);10601061 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;1062 Self::validate_create_item_args(&target_collection, &data)?;1063 Self::create_item_no_validation(collection_id, owner, data)?;10641065 Ok(())1066 }10671068 /// This method creates multiple items in a collection created with CreateCollection method.1069 /// 1070 /// # Permissions1071 /// 1072 /// * Collection Owner.1073 /// * Collection Admin.1074 /// * Anyone if1075 /// * White List is enabled, and1076 /// * Address is added to white list, and1077 /// * MintPermission is enabled (see SetMintPermission method)1078 /// 1079 /// # Arguments1080 /// 1081 /// * collection_id: ID of the collection.1082 /// 1083 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1084 /// 1085 /// * owner: Address, initial owner of the NFT.1086 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1087 .map(|data| { data.len() })1088 .sum())]1089 #[transactional]1090 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10911092 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1093 let sender = ensure_signed(origin)?;10941095 Self::collection_exists(collection_id)?;1096 let target_collection = <Collection<T>>::get(collection_id);10971098 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10991100 for data in &items_data {1101 Self::validate_create_item_args(&target_collection, data)?;1102 }1103 for data in &items_data {1104 Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1105 }11061107 Ok(())1108 }11091110 /// Destroys a concrete instance of NFT.1111 /// 1112 /// # Permissions1113 /// 1114 /// * Collection Owner.1115 /// * Collection Admin.1116 /// * Current NFT Owner.1117 /// 1118 /// # Arguments1119 /// 1120 /// * collection_id: ID of the collection.1121 /// 1122 /// * item_id: ID of NFT to burn.1123 #[weight = <T as Config>::WeightInfo::burn_item()]1124 #[transactional]1125 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11261127 let sender = ensure_signed(origin)?;1128 Self::collection_exists(collection_id)?;11291130 // Transfer permissions check1131 let target_collection = <Collection<T>>::get(collection_id);1132 ensure!(1133 Self::is_item_owner(sender.clone(), collection_id, item_id) ||1134 (1135 target_collection.limits.owner_can_transfer &&1136 Self::is_owner_or_admin_permissions(collection_id, sender.clone())1137 ),1138 Error::<T>::NoPermission1139 );11401141 if target_collection.access == AccessMode::WhiteList {1142 Self::check_white_list(collection_id, &sender)?;1143 }11441145 match target_collection.mode1146 {1147 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1148 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,1149 CollectionMode::ReFungible => Self::burn_refungible_item(collection_id, item_id, &sender)?,1150 _ => ()1151 };11521153 // call event1154 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));11551156 Ok(())1157 }11581159 /// Change ownership of the token.1160 /// 1161 /// # Permissions1162 /// 1163 /// * Collection Owner1164 /// * Collection Admin1165 /// * Current NFT owner1166 ///1167 /// # Arguments1168 /// 1169 /// * recipient: Address of token recipient.1170 /// 1171 /// * collection_id.1172 /// 1173 /// * item_id: ID of the item1174 /// * Non-Fungible Mode: Required.1175 /// * Fungible Mode: Ignored.1176 /// * Re-Fungible Mode: Required.1177 /// 1178 /// * value: Amount to transfer.1179 /// * Non-Fungible Mode: Ignored1180 /// * Fungible Mode: Must specify transferred amount1181 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1182 #[weight = <T as Config>::WeightInfo::transfer()]1183 #[transactional]1184 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1185 let sender = ensure_signed(origin)?;1186 Self::transfer_internal(sender, recipient, collection_id, item_id, value)1187 }11881189 /// Set, change, or remove approved address to transfer the ownership of the NFT.1190 /// 1191 /// # Permissions1192 /// 1193 /// * Collection Owner1194 /// * Collection Admin1195 /// * Current NFT owner1196 /// 1197 /// # Arguments1198 /// 1199 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1200 /// 1201 /// * collection_id.1202 /// 1203 /// * item_id: ID of the item.1204 #[weight = <T as Config>::WeightInfo::approve()]1205 #[transactional]1206 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12071208 let sender = ensure_signed(origin)?;12091210 Self::collection_exists(collection_id)?;1211 Self::token_exists(collection_id, item_id, &sender)?;12121213 // Transfer permissions check1214 let target_collection = <Collection<T>>::get(collection_id);1215 let allowance_limit = if target_collection.limits.owner_can_transfer &&1216 Self::is_owner_or_admin_permissions(1217 collection_id,1218 sender.clone(),1219 ) {1220 None1221 } else if let Some(amount) = Self::owned_amount(1222 sender.clone(),1223 collection_id,1224 item_id,1225 ) {1226 Some(amount)1227 } else {1228 fail!(Error::<T>::NoPermission);1229 };12301231 if target_collection.access == AccessMode::WhiteList {1232 Self::check_white_list(collection_id, &sender)?;1233 Self::check_white_list(collection_id, &spender)?;1234 }12351236 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1237 let mut allowance: u128 = amount;1238 if allowance_exists {1239 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1240 }1241 if let Some(limit) = allowance_limit {1242 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1243 }1244 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12451246 Ok(())1247 }1248 1249 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1250 /// 1251 /// # Permissions1252 /// * Collection Owner1253 /// * Collection Admin1254 /// * Current NFT owner1255 /// * Address approved by current NFT owner1256 /// 1257 /// # Arguments1258 /// 1259 /// * from: Address that owns token.1260 /// 1261 /// * recipient: Address of token recipient.1262 /// 1263 /// * collection_id.1264 /// 1265 /// * item_id: ID of the item.1266 /// 1267 /// * value: Amount to transfer.1268 #[weight = <T as Config>::WeightInfo::transfer_from()]1269 #[transactional]1270 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12711272 let sender = ensure_signed(origin)?;1273 let mut appoved_transfer = false;12741275 // Check approval1276 let mut approval: u128 = 0;1277 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1278 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1279 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1280 appoved_transfer = true;1281 }12821283 let target_collection = <Collection<T>>::get(collection_id);12841285 // Limits check1286 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12871288 // Transfer permissions check 1289 ensure!(1290 appoved_transfer || 1291 (1292 target_collection.limits.owner_can_transfer &&1293 Self::is_owner_or_admin_permissions(collection_id, sender.clone())1294 ),1295 Error::<T>::NoPermission1296 );12971298 if target_collection.access == AccessMode::WhiteList {1299 Self::check_white_list(collection_id, &sender)?;1300 Self::check_white_list(collection_id, &recipient)?;1301 }13021303 // Reduce approval by transferred amount or remove if remaining approval drops to 01304 if approval.checked_sub(value).unwrap_or(0) > 0 {1305 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1306 }1307 else {1308 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1309 }13101311 match target_collection.mode1312 {1313 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1314 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1315 CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1316 _ => ()1317 };13181319 Ok(())1320 }13211322 // #[weight = 0]1323 // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13241325 // // let no_perm_mes = "You do not have permissions to modify this collection";1326 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1327 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1328 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13291330 // // // on_nft_received call13311332 // // Self::transfer(origin, collection_id, item_id, new_owner)?;13331334 // Ok(())1335 // }13361337 /// Set off-chain data schema.1338 /// 1339 /// # Permissions1340 /// 1341 /// * Collection Owner1342 /// * Collection Admin1343 /// 1344 /// # Arguments1345 /// 1346 /// * collection_id.1347 /// 1348 /// * schema: String representing the offchain data schema.1349 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1350 #[transactional]1351 pub fn set_variable_meta_data (1352 origin,1353 collection_id: CollectionId,1354 item_id: TokenId,1355 data: Vec<u8>1356 ) -> DispatchResult {1357 let sender = ensure_signed(origin)?;1358 1359 Self::collection_exists(collection_id)?;1360 Self::token_exists(collection_id, item_id, &sender)?;13611362 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13631364 // Modify permissions check1365 let target_collection = <Collection<T>>::get(collection_id);1366 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1367 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1368 Error::<T>::NoPermission);13691370 match target_collection.mode1371 {1372 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1373 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1374 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1375 _ => fail!(Error::<T>::UnexpectedCollectionType)1376 };13771378 Ok(())1379 }1380 1381 /// Set schema standard1382 /// ImageURL1383 /// Unique1384 /// 1385 /// # Permissions1386 /// 1387 /// * Collection Owner1388 /// * Collection Admin1389 /// 1390 /// # Arguments1391 /// 1392 /// * collection_id.1393 /// 1394 /// * schema: SchemaVersion: enum1395 #[weight = <T as Config>::WeightInfo::set_schema_version()]1396 #[transactional]1397 pub fn set_schema_version(1398 origin,1399 collection_id: CollectionId,1400 version: SchemaVersion1401 ) -> DispatchResult {1402 let sender = ensure_signed(origin)?;1403 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1404 let mut target_collection = <Collection<T>>::get(collection_id);1405 target_collection.schema_version = version;1406 <Collection<T>>::insert(collection_id, target_collection);14071408 Ok(())1409 }14101411 /// Set off-chain data schema.1412 /// 1413 /// # Permissions1414 /// 1415 /// * Collection Owner1416 /// * Collection Admin1417 /// 1418 /// # Arguments1419 /// 1420 /// * collection_id.1421 /// 1422 /// * schema: String representing the offchain data schema.1423 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1424 #[transactional]1425 pub fn set_offchain_schema(1426 origin,1427 collection_id: CollectionId,1428 schema: Vec<u8>1429 ) -> DispatchResult {1430 let sender = ensure_signed(origin)?;1431 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14321433 // check schema limit1434 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14351436 let mut target_collection = <Collection<T>>::get(collection_id);1437 target_collection.offchain_schema = schema;1438 <Collection<T>>::insert(collection_id, target_collection);14391440 Ok(())1441 }14421443 /// Set const on-chain data schema.1444 /// 1445 /// # Permissions1446 /// 1447 /// * Collection Owner1448 /// * Collection Admin1449 /// 1450 /// # Arguments1451 /// 1452 /// * collection_id.1453 /// 1454 /// * schema: String representing the const on-chain data schema.1455 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1456 #[transactional]1457 pub fn set_const_on_chain_schema (1458 origin,1459 collection_id: CollectionId,1460 schema: Vec<u8>1461 ) -> DispatchResult {1462 let sender = ensure_signed(origin)?;1463 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14641465 // check schema limit1466 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14671468 let mut target_collection = <Collection<T>>::get(collection_id);1469 target_collection.const_on_chain_schema = schema;1470 <Collection<T>>::insert(collection_id, target_collection);14711472 Ok(())1473 }14741475 /// Set variable on-chain data schema.1476 /// 1477 /// # Permissions1478 /// 1479 /// * Collection Owner1480 /// * Collection Admin1481 /// 1482 /// # Arguments1483 /// 1484 /// * collection_id.1485 /// 1486 /// * schema: String representing the variable on-chain data schema.1487 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1488 #[transactional]1489 pub fn set_variable_on_chain_schema (1490 origin,1491 collection_id: CollectionId,1492 schema: Vec<u8>1493 ) -> DispatchResult {1494 let sender = ensure_signed(origin)?;1495 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14961497 // check schema limit1498 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14991500 let mut target_collection = <Collection<T>>::get(collection_id);1501 target_collection.variable_on_chain_schema = schema;1502 <Collection<T>>::insert(collection_id, target_collection);15031504 Ok(())1505 }15061507 // Sudo permissions function1508 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1509 #[transactional]1510 pub fn set_chain_limits(1511 origin,1512 limits: ChainLimits1513 ) -> DispatchResult {15141515 #[cfg(not(feature = "runtime-benchmarks"))]1516 ensure_root(origin)?;15171518 <ChainLimit>::put(limits);1519 Ok(())1520 }15211522 /// Enable smart contract self-sponsoring.1523 /// 1524 /// # Permissions1525 /// 1526 /// * Contract Owner1527 /// 1528 /// # Arguments1529 /// 1530 /// * contract address1531 /// * enable flag1532 /// 1533 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1534 #[transactional]1535 pub fn enable_contract_sponsoring(1536 origin,1537 contract_address: T::AccountId,1538 enable: bool1539 ) -> DispatchResult {15401541 let sender = ensure_signed(origin)?;15421543 #[cfg(feature = "runtime-benchmarks")]1544 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15451546 Self::ensure_contract_owned(sender, &contract_address)?;15471548 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1549 Ok(())1550 }15511552 /// Set the rate limit for contract sponsoring to specified number of blocks.1553 /// 1554 /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1555 /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1556 /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1557 /// from contract endowment if there are at least B blocks between such transactions. 1558 /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1559 /// 1560 /// # Permissions1561 /// 1562 /// * Contract Owner1563 /// 1564 /// # Arguments1565 /// 1566 /// -`contract_address`: Address of the contract to sponsor1567 /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1568 /// 1569 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1570 #[transactional]1571 pub fn set_contract_sponsoring_rate_limit(1572 origin,1573 contract_address: T::AccountId,1574 rate_limit: T::BlockNumber1575 ) -> DispatchResult {1576 let sender = ensure_signed(origin)?;15771578 #[cfg(feature = "runtime-benchmarks")]1579 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15801581 Self::ensure_contract_owned(sender, &contract_address)?;1582 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1583 Ok(())1584 }15851586 /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1587 /// 1588 /// # Permissions1589 /// 1590 /// * Address that deployed smart contract.1591 /// 1592 /// # Arguments1593 /// 1594 /// -`contract_address`: Address of the contract.1595 /// 1596 /// - `enable`: . 1597 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1598 #[transactional]1599 pub fn toggle_contract_white_list(1600 origin,1601 contract_address: T::AccountId,1602 enable: bool1603 ) -> DispatchResult {1604 let sender = ensure_signed(origin)?;16051606 #[cfg(feature = "runtime-benchmarks")]1607 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16081609 Self::ensure_contract_owned(sender, &contract_address)?;1610 <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1611 Ok(())1612 }1613 1614 /// Add an address to smart contract white list.1615 /// 1616 /// # Permissions1617 /// 1618 /// * Address that deployed smart contract.1619 /// 1620 /// # Arguments1621 /// 1622 /// -`contract_address`: Address of the contract.1623 ///1624 /// -`account_address`: Address to add.1625 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1626 #[transactional]1627 pub fn add_to_contract_white_list(1628 origin,1629 contract_address: T::AccountId,1630 account_address: T::AccountId1631 ) -> DispatchResult {1632 let sender = ensure_signed(origin)?;16331634 #[cfg(feature = "runtime-benchmarks")]1635 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1636 1637 Self::ensure_contract_owned(sender, &contract_address)?; 1638 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1639 Ok(())1640 }16411642 /// Remove an address from smart contract white list.1643 /// 1644 /// # Permissions1645 /// 1646 /// * Address that deployed smart contract.1647 /// 1648 /// # Arguments1649 /// 1650 /// -`contract_address`: Address of the contract.1651 ///1652 /// -`account_address`: Address to remove.1653 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1654 #[transactional]1655 pub fn remove_from_contract_white_list(1656 origin,1657 contract_address: T::AccountId,1658 account_address: T::AccountId1659 ) -> DispatchResult {1660 let sender = ensure_signed(origin)?;16611662 #[cfg(feature = "runtime-benchmarks")]1663 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16641665 Self::ensure_contract_owned(sender, &contract_address)?;1666 <ContractWhiteList<T>>::remove(contract_address, account_address);1667 Ok(())1668 }16691670 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1671 #[transactional]1672 pub fn set_collection_limits(1673 origin,1674 collection_id: u32,1675 new_limits: CollectionLimits,1676 ) -> DispatchResult {1677 let sender = ensure_signed(origin)?;1678 Self::check_owner_permissions(collection_id, sender.clone())?;1679 let mut target_collection = <Collection<T>>::get(collection_id);1680 let old_limits = target_collection.limits;1681 let chain_limits = ChainLimit::get();16821683 // collection bounds1684 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1685 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1686 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1687 Error::<T>::CollectionLimitBoundsExceeded);16881689 // token_limit check prev1690 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1691 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16921693 ensure!(1694 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1695 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1696 Error::<T>::OwnerPermissionsCantBeReverted,1697 );16981699 target_collection.limits = new_limits;1700 <Collection<T>>::insert(collection_id, target_collection);17011702 Ok(())1703 } 1704 }1705}17061707impl<T: Config> Module<T> {17081709 pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {17101711 let target_collection = <Collection<T>>::get(collection_id);17121713 // Limits check1714 Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;17151716 // Transfer permissions check1717 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1718 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1719 Error::<T>::NoPermission);17201721 if target_collection.access == AccessMode::WhiteList {1722 Self::check_white_list(collection_id, &sender)?;1723 Self::check_white_list(collection_id, &recipient)?;1724 }17251726 match target_collection.mode1727 {1728 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1729 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1730 CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1731 _ => ()1732 };17331734 Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));17351736 Ok(())1737 }173817391740 fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {17411742 // check token limit and account token limit1743 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1744 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1745 1746 Ok(())1747 }17481749 fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {17501751 // check token limit and account token limit1752 let total_items: u32 = ItemListIndex::get(collection_id);1753 let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1754 ensure!(collection.limits.token_limit > total_items, Error::<T>::CollectionTokenLimitExceeded);1755 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);17561757 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1758 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1759 Self::check_white_list(collection_id, owner)?;1760 Self::check_white_list(collection_id, sender)?;1761 }17621763 Ok(())1764 }17651766 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1767 match target_collection.mode1768 {1769 CollectionMode::NFT => {1770 if let CreateItemData::NFT(data) = data {1771 // check sizes1772 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1773 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1774 } else {1775 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1776 }1777 },1778 CollectionMode::Fungible(_) => {1779 if let CreateItemData::Fungible(_) = data {1780 } else {1781 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1782 }1783 },1784 CollectionMode::ReFungible => {1785 if let CreateItemData::ReFungible(data) = data {17861787 // check sizes1788 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1789 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17901791 // Check refungibility limits1792 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1793 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1794 } else {1795 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1796 }1797 },1798 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1799 };18001801 Ok(())1802 }18031804 fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1805 match data1806 {1807 CreateItemData::NFT(data) => {1808 let item = NftItemType {1809 owner: owner.clone(),1810 const_data: data.const_data,1811 variable_data: data.variable_data1812 };18131814 Self::add_nft_item(collection_id, item)?;1815 },1816 CreateItemData::Fungible(data) => {1817 Self::add_fungible_item(collection_id, &owner, data.value)?;1818 },1819 CreateItemData::ReFungible(data) => {1820 let mut owner_list = Vec::new();1821 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});18221823 let item = ReFungibleItemType {1824 owner: owner_list,1825 const_data: data.const_data,1826 variable_data: data.variable_data1827 };18281829 Self::add_refungible_item(collection_id, item)?;1830 }1831 };18321833 Ok(())1834 }18351836 fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {18371838 // Does new owner already have an account?1839 let mut balance: u128 = 0;1840 if <FungibleItemList<T>>::contains_key(collection_id, owner) {1841 balance = <FungibleItemList<T>>::get(collection_id, owner).value;1842 } 18431844 // Mint 1845 let item = FungibleItemType {1846 value: balance + value1847 };1848 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);18491850 // Update balance1851 let new_balance = <Balance<T>>::get(collection_id, owner)1852 .checked_add(value)1853 .ok_or(Error::<T>::NumOverflow)?;1854 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18551856 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1857 Ok(())1858 }18591860 fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1861 let current_index = <ItemListIndex>::get(collection_id)1862 .checked_add(1)1863 .ok_or(Error::<T>::NumOverflow)?;1864 let itemcopy = item.clone();18651866 let value = item.owner.first().unwrap().fraction;1867 let owner = item.owner.first().unwrap().owner.clone();18681869 Self::add_token_index(collection_id, current_index, &owner)?;18701871 <ItemListIndex>::insert(collection_id, current_index);1872 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18731874 // Update balance1875 let new_balance = <Balance<T>>::get(collection_id, &owner)1876 .checked_add(value)1877 .ok_or(Error::<T>::NumOverflow)?;1878 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);18791880 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1881 Ok(())1882 }18831884 fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1885 let current_index = <ItemListIndex>::get(collection_id)1886 .checked_add(1)1887 .ok_or(Error::<T>::NumOverflow)?;18881889 let item_owner = item.owner.clone();1890 Self::add_token_index(collection_id, current_index, &item.owner)?;18911892 <ItemListIndex>::insert(collection_id, current_index);1893 <NftItemList<T>>::insert(collection_id, current_index, item);18941895 // Update balance1896 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1897 .checked_add(1)1898 .ok_or(Error::<T>::NumOverflow)?;1899 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);19001901 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1902 Ok(())1903 }19041905 fn burn_refungible_item(1906 collection_id: CollectionId,1907 item_id: TokenId,1908 owner: &T::AccountId,1909 ) -> DispatchResult {1910 ensure!(1911 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1912 Error::<T>::TokenNotFound1913 );1914 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1915 let rft_balance = token1916 .owner1917 .iter()1918 .filter(|&i| i.owner == *owner)1919 .next()1920 .unwrap();1921 Self::remove_token_index(collection_id, item_id, owner)?;19221923 // update balance1924 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1925 .checked_sub(rft_balance.fraction)1926 .ok_or(Error::<T>::NumOverflow)?;1927 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);19281929 // Re-create owners list with sender removed1930 let index = token1931 .owner1932 .iter()1933 .position(|i| i.owner == *owner)1934 .unwrap();1935 token.owner.remove(index);1936 let owner_count = token.owner.len();19371938 // Burn the token completely if this was the last (only) owner1939 if owner_count == 0 {1940 <ReFungibleItemList<T>>::remove(collection_id, item_id);1941 }1942 else {1943 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1944 }19451946 Ok(())1947 }19481949 fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1950 ensure!(1951 <NftItemList<T>>::contains_key(collection_id, item_id),1952 Error::<T>::TokenNotFound1953 );1954 let item = <NftItemList<T>>::get(collection_id, item_id);1955 Self::remove_token_index(collection_id, item_id, &item.owner)?;19561957 // update balance1958 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1959 .checked_sub(1)1960 .ok_or(Error::<T>::NumOverflow)?;1961 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1962 <NftItemList<T>>::remove(collection_id, item_id);19631964 Ok(())1965 }19661967 fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1968 ensure!(1969 <FungibleItemList<T>>::contains_key(collection_id, owner),1970 Error::<T>::TokenNotFound1971 );1972 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1973 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19741975 // update balance1976 let new_balance = <Balance<T>>::get(collection_id, owner)1977 .checked_sub(value)1978 .ok_or(Error::<T>::NumOverflow)?;1979 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19801981 if balance.value - value > 0 {1982 balance.value -= value;1983 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1984 }1985 else {1986 <FungibleItemList<T>>::remove(collection_id, owner);1987 }19881989 Ok(())1990 }19911992 fn collection_exists(collection_id: CollectionId) -> DispatchResult {1993 ensure!(1994 <Collection<T>>::contains_key(collection_id),1995 Error::<T>::CollectionNotFound1996 );1997 Ok(())1998 }19992000 fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {2001 Self::collection_exists(collection_id)?;20022003 let target_collection = <Collection<T>>::get(collection_id);2004 ensure!(2005 subject == target_collection.owner,2006 Error::<T>::NoPermission2007 );20082009 Ok(())2010 }20112012 fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {2013 let target_collection = <Collection<T>>::get(collection_id);2014 let mut result: bool = subject == target_collection.owner;2015 let exists = <AdminList<T>>::contains_key(collection_id);20162017 if !result & exists {2018 if <AdminList<T>>::get(collection_id).contains(&subject) {2019 result = true2020 }2021 }20222023 result2024 }20252026 fn check_owner_or_admin_permissions(2027 collection_id: CollectionId,2028 subject: T::AccountId,2029 ) -> DispatchResult {2030 Self::collection_exists(collection_id)?;2031 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());20322033 ensure!(2034 result,2035 Error::<T>::NoPermission2036 );2037 Ok(())2038 }20392040 fn owned_amount(2041 subject: T::AccountId,2042 collection_id: CollectionId,2043 item_id: TokenId,2044 ) -> Option<u128> {2045 let target_collection = <Collection<T>>::get(collection_id);20462047 match target_collection.mode {2048 CollectionMode::NFT => {2049 if <NftItemList<T>>::get(collection_id, item_id).owner == subject {2050 return Some(1)2051 }2052 None2053 },2054 CollectionMode::Fungible(_) => {2055 if <FungibleItemList<T>>::contains_key(collection_id, &subject) {2056 return Some(<FungibleItemList<T>>::get(collection_id, &subject)2057 .value);2058 }2059 None2060 },2061 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)2062 .owner2063 .iter()2064 .find(|i| i.owner == subject)2065 .map(|i| i.fraction),2066 CollectionMode::Invalid => None,2067 }2068 }20692070 fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {2071 let target_collection = <Collection<T>>::get(collection_id);20722073 match target_collection.mode {2074 CollectionMode::NFT => {2075 <NftItemList<T>>::get(collection_id, item_id).owner == subject2076 }2077 CollectionMode::Fungible(_) => {2078 <FungibleItemList<T>>::contains_key(collection_id, &subject)2079 }2080 CollectionMode::ReFungible => {2081 <ReFungibleItemList<T>>::get(collection_id, item_id)2082 .owner2083 .iter()2084 .any(|i| i.owner == subject)2085 }2086 CollectionMode::Invalid => false,2087 }2088 }20892090 fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {2091 let mes = Error::<T>::AddresNotInWhiteList;2092 ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20932094 Ok(())2095 }20962097 /// Check if token exists. In case of Fungible, check if there is an entry for 2098 /// the owner in fungible balances double map2099 fn token_exists(2100 collection_id: CollectionId,2101 item_id: TokenId,2102 owner: &T::AccountId2103 ) -> DispatchResult {2104 let target_collection = <Collection<T>>::get(collection_id);2105 let exists = match target_collection.mode2106 {2107 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2108 CollectionMode::Fungible(_) => <FungibleItemList<T>>::contains_key(collection_id, owner),2109 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2110 _ => false2111 };21122113 ensure!(exists == true, Error::<T>::TokenNotFound);2114 Ok(())2115 }21162117 fn transfer_fungible(2118 collection_id: CollectionId,2119 value: u128,2120 owner: &T::AccountId,2121 recipient: &T::AccountId,2122 ) -> DispatchResult {2123 Self::token_exists(collection_id, 0, owner)?;21242125 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2126 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);21272128 // Send balance to recipient (updates balanceOf of recipient)2129 Self::add_fungible_item(collection_id, recipient, value)?;21302131 // update balanceOf of sender2132 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);21332134 // Reduce or remove sender2135 if balance.value == value {2136 <FungibleItemList<T>>::remove(collection_id, owner);2137 }2138 else {2139 balance.value -= value;2140 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2141 }21422143 Ok(())2144 }21452146 fn transfer_refungible(2147 collection_id: CollectionId,2148 item_id: TokenId,2149 value: u128,2150 owner: T::AccountId,2151 new_owner: T::AccountId,2152 ) -> DispatchResult {2153 Self::token_exists(collection_id, item_id, &owner)?;21542155 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2156 let item = full_item2157 .owner2158 .iter()2159 .filter(|i| i.owner == owner)2160 .next()2161 .ok_or(Error::<T>::NumOverflow)?;2162 let amount = item.fraction;21632164 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21652166 // update balance2167 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2168 .checked_sub(value)2169 .ok_or(Error::<T>::NumOverflow)?;2170 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21712172 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2173 .checked_add(value)2174 .ok_or(Error::<T>::NumOverflow)?;2175 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21762177 let old_owner = item.owner.clone();2178 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21792180 // transfer2181 if amount == value && !new_owner_has_account {2182 // change owner2183 // new owner do not have account2184 let mut new_full_item = full_item.clone();2185 new_full_item2186 .owner2187 .iter_mut()2188 .find(|i| i.owner == owner)2189 .unwrap()2190 .owner = new_owner.clone();2191 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21922193 // update index collection2194 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2195 } else {2196 let mut new_full_item = full_item.clone();2197 new_full_item2198 .owner2199 .iter_mut()2200 .find(|i| i.owner == owner)2201 .unwrap()2202 .fraction -= value;22032204 // separate amount2205 if new_owner_has_account {2206 // new owner has account2207 new_full_item2208 .owner2209 .iter_mut()2210 .find(|i| i.owner == new_owner)2211 .unwrap()2212 .fraction += value;2213 } else {2214 // new owner do not have account2215 new_full_item.owner.push(Ownership {2216 owner: new_owner.clone(),2217 fraction: value,2218 });2219 Self::add_token_index(collection_id, item_id, &new_owner)?;2220 }22212222 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2223 }22242225 Ok(())2226 }22272228 fn transfer_nft(2229 collection_id: CollectionId,2230 item_id: TokenId,2231 sender: T::AccountId,2232 new_owner: T::AccountId,2233 ) -> DispatchResult {2234 Self::token_exists(collection_id, item_id, &sender)?;22352236 let mut item = <NftItemList<T>>::get(collection_id, item_id);22372238 ensure!(2239 sender == item.owner,2240 Error::<T>::MustBeTokenOwner2241 );22422243 // update balance2244 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2245 .checked_sub(1)2246 .ok_or(Error::<T>::NumOverflow)?;2247 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22482249 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2250 .checked_add(1)2251 .ok_or(Error::<T>::NumOverflow)?;2252 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);22532254 // change owner2255 let old_owner = item.owner.clone();2256 item.owner = new_owner.clone();2257 <NftItemList<T>>::insert(collection_id, item_id, item);22582259 // update index collection2260 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22612262 Ok(())2263 }2264 2265 fn set_re_fungible_variable_data(2266 collection_id: CollectionId,2267 item_id: TokenId,2268 data: Vec<u8>2269 ) -> DispatchResult {2270 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);22712272 item.variable_data = data;22732274 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22752276 Ok(())2277 }22782279 fn set_nft_variable_data(2280 collection_id: CollectionId,2281 item_id: TokenId,2282 data: Vec<u8>2283 ) -> DispatchResult {2284 let mut item = <NftItemList<T>>::get(collection_id, item_id);2285 2286 item.variable_data = data;22872288 <NftItemList<T>>::insert(collection_id, item_id, item);2289 2290 Ok(())2291 }22922293 fn init_collection(item: &CollectionType<T::AccountId>) {2294 // check params2295 assert!(2296 item.decimal_points <= MAX_DECIMAL_POINTS,2297 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2298 );2299 assert!(2300 item.name.len() <= 64,2301 "Collection name can not be longer than 63 char"2302 );2303 assert!(2304 item.name.len() <= 256,2305 "Collection description can not be longer than 255 char"2306 );2307 assert!(2308 item.token_prefix.len() <= 16,2309 "Token prefix can not be longer than 15 char"2310 );23112312 // Generate next collection ID2313 let next_id = CreatedCollectionCount::get()2314 .checked_add(1)2315 .unwrap();23162317 CreatedCollectionCount::put(next_id);2318 }23192320 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2321 let current_index = <ItemListIndex>::get(collection_id)2322 .checked_add(1)2323 .unwrap();23242325 let item_owner = item.owner.clone();2326 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();23272328 <ItemListIndex>::insert(collection_id, current_index);23292330 // Update balance2331 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2332 .checked_add(1)2333 .unwrap();2334 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2335 }23362337 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2338 let current_index = <ItemListIndex>::get(collection_id)2339 .checked_add(1)2340 .unwrap();23412342 Self::add_token_index(collection_id, current_index, owner).unwrap();23432344 <ItemListIndex>::insert(collection_id, current_index);23452346 // Update balance2347 let new_balance = <Balance<T>>::get(collection_id, owner)2348 .checked_add(item.value)2349 .unwrap();2350 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2351 }23522353 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2354 let current_index = <ItemListIndex>::get(collection_id)2355 .checked_add(1)2356 .unwrap();23572358 let value = item.owner.first().unwrap().fraction;2359 let owner = item.owner.first().unwrap().owner.clone();23602361 Self::add_token_index(collection_id, current_index, &owner).unwrap();23622363 <ItemListIndex>::insert(collection_id, current_index);23642365 // Update balance2366 let new_balance = <Balance<T>>::get(collection_id, &owner)2367 .checked_add(value)2368 .unwrap();2369 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2370 }23712372 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {23732374 // add to account limit2375 if <AccountItemCount<T>>::contains_key(owner) {23762377 // bound Owned tokens by a single address2378 let count = <AccountItemCount<T>>::get(owner);2379 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23802381 <AccountItemCount<T>>::insert(owner.clone(), count2382 .checked_add(1)2383 .ok_or(Error::<T>::NumOverflow)?);2384 }2385 else {2386 <AccountItemCount<T>>::insert(owner.clone(), 1);2387 }23882389 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2390 if list_exists {2391 let mut list = <AddressTokens<T>>::get(collection_id, owner);2392 let item_contains = list.contains(&item_index.clone());23932394 if !item_contains {2395 list.push(item_index.clone());2396 }23972398 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2399 } else {2400 let mut itm = Vec::new();2401 itm.push(item_index.clone());2402 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2403 }24042405 Ok(())2406 }24072408 fn remove_token_index(2409 collection_id: CollectionId,2410 item_index: TokenId,2411 owner: &T::AccountId,2412 ) -> DispatchResult {24132414 // update counter2415 <AccountItemCount<T>>::insert(owner.clone(), 2416 <AccountItemCount<T>>::get(owner)2417 .checked_sub(1)2418 .ok_or(Error::<T>::NumOverflow)?);241924202421 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2422 if list_exists {2423 let mut list = <AddressTokens<T>>::get(collection_id, owner);2424 let item_contains = list.contains(&item_index.clone());24252426 if item_contains {2427 list.retain(|&item| item != item_index);2428 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2429 }2430 }24312432 Ok(())2433 }24342435 fn move_token_index(2436 collection_id: CollectionId,2437 item_index: TokenId,2438 old_owner: &T::AccountId,2439 new_owner: &T::AccountId,2440 ) -> DispatchResult {2441 Self::remove_token_index(collection_id, item_index, old_owner)?;2442 Self::add_token_index(collection_id, item_index, new_owner)?;24432444 Ok(())2445 }2446 2447 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2448 if <ContractOwner<T>>::contains_key(contract.clone()) {2449 let owner = <ContractOwner<T>>::get(contract);2450 ensure!(account == owner, Error::<T>::NoPermission);2451 } else {2452 fail!(Error::<T>::NoPermission);2453 }24542455 Ok(())2456 }2457}24582459////////////////////////////////////////////////////////////////////////////////////////////////////2460// Economic models2461// #region24622463/// Fee multiplier.2464pub type Multiplier = FixedU128;24652466type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24672468/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2469/// in the queue.2470#[derive(Encode, Decode, Clone, Eq, PartialEq)]2471pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);24722473impl<T: Config + Send + Sync> sp_std::fmt::Debug 2474 for ChargeTransactionPayment<T>2475{2476 #[cfg(feature = "std")]2477 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2478 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2479 }2480 #[cfg(not(feature = "std"))]2481 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2482 Ok(())2483 }2484}24852486impl<T: Config> ChargeTransactionPayment<T>2487where2488 T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2489 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2490 T::AccountId: AsRef<[u8]>,2491 T::AccountId: UncheckedFrom<T::Hash>,2492{2493 fn traditional_fee(2494 len: usize,2495 info: &DispatchInfoOf<T::Call>,2496 tip: BalanceOf<T>,2497 ) -> BalanceOf<T>2498 where2499 T::Call: Dispatchable<Info = DispatchInfo>,2500 {2501 <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2502 }25032504 fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2505 let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2506 let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2507 let len_saturation = max_block_length as u64 / (len as u64).max(1);2508 let coefficient: BalanceOf<T> = weight_saturation2509 .min(len_saturation)2510 .saturated_into::<BalanceOf<T>>();2511 final_fee2512 .saturating_mul(coefficient)2513 .saturated_into::<TransactionPriority>()2514 }25152516 fn withdraw_fee(2517 &self,2518 who: &T::AccountId,2519 call: &T::Call,2520 info: &DispatchInfoOf<T::Call>,2521 len: usize,2522 ) -> Result<2523 (2524 BalanceOf<T>,2525 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2526 ),2527 TransactionValidityError,2528 > {2529 let tip = self.0;25302531 // Set fee based on call type. Creating collection costs 1 Unique.2532 // All other transactions have traditional fees so far2533 // let fee = match call.is_sub_type() {2534 // Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2535 // _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2536 // // _ => <BalanceOf<T>>::from(100)2537 // };2538 let fee = Self::traditional_fee(len, info, tip);25392540 // Only mess with balances if fee is not zero.2541 if fee.is_zero() {2542 return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2543 .map(|i| (fee, i));2544 }25452546 // Determine who is paying transaction fee based on ecnomic model2547 // Parse call to extract collection ID and access collection sponsor2548 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2549 Some(Call::create_item(collection_id, _owner, _properties)) => {25502551 // sponsor timeout2552 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25532554 let collection = <Collection<T>>::get(collection_id);25552556 let limit = collection.limits.sponsor_transfer_timeout;2557 let mut sponsored = true;2558 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2559 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2560 let limit_time = last_tx_block + limit.into();2561 if block_number <= limit_time {2562 sponsored = false;2563 }2564 }2565 if sponsored {2566 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2567 }25682569 // check free create limit2570 if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2571 (sponsored)2572 {2573 collection.sponsorship.sponsor()2574 .cloned()2575 .unwrap_or_default()2576 } else {2577 T::AccountId::default()2578 }2579 }2580 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2581 2582 let mut sponsor_transfer = false;2583 if <Collection<T>>::get(collection_id).sponsorship.confirmed() {25842585 let collection_limits = <Collection<T>>::get(collection_id).limits;2586 let collection_mode = <Collection<T>>::get(collection_id).mode;2587 2588 // sponsor timeout2589 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2590 sponsor_transfer = match collection_mode {2591 CollectionMode::NFT => {2592 2593 // get correct limit2594 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2595 collection_limits.sponsor_transfer_timeout2596 } else {2597 ChainLimit::get().nft_sponsor_transfer_timeout2598 };2599 2600 let mut sponsored = true;2601 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2602 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2603 let limit_time = last_tx_block + limit.into();2604 if block_number <= limit_time {2605 sponsored = false;2606 }2607 }2608 if sponsored {2609 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2610 }26112612 sponsored2613 }2614 CollectionMode::Fungible(_) => {2615 2616 // get correct limit2617 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2618 collection_limits.sponsor_transfer_timeout2619 } else {2620 ChainLimit::get().fungible_sponsor_transfer_timeout2621 };2622 2623 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2624 let mut sponsored = true;2625 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2626 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2627 let limit_time = last_tx_block + limit.into();2628 if block_number <= limit_time {2629 sponsored = false;2630 }2631 }2632 if sponsored {2633 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2634 }26352636 sponsored2637 }2638 CollectionMode::ReFungible => {2639 2640 // get correct limit2641 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2642 collection_limits.sponsor_transfer_timeout2643 } else {2644 ChainLimit::get().refungible_sponsor_transfer_timeout2645 };2646 2647 let mut sponsored = true;2648 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2649 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2650 let limit_time = last_tx_block + limit.into();2651 if block_number <= limit_time {2652 sponsored = false;2653 }2654 }2655 if sponsored {2656 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2657 }26582659 sponsored2660 }2661 _ => {2662 false2663 },2664 };2665 }26662667 if !sponsor_transfer {2668 T::AccountId::default()2669 } else {2670 <Collection<T>>::get(collection_id).sponsorship.sponsor()2671 .cloned()2672 .unwrap_or_default()2673 }2674 }26752676 _ => T::AccountId::default(),2677 };26782679 // Sponsor smart contracts2680 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26812682 // On instantiation: set the contract owner2683 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26842685 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2686 &who,2687 code_hash,2688 salt,2689 );2690 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26912692 T::AccountId::default()2693 },26942695 // On instantiation with code: set the contract owner2696 Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {26972698 let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2699 &who,2700 &T::Hashing::hash(&_code),2701 _salt,2702 );27032704 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());27052706 T::AccountId::default()2707 }27082709 // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2710 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27112712 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27132714 let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2715 && <ContractOwner<T>>::get(called_contract.clone()) == *who;2716 let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2717 2718 if !owned_contract && white_list_enabled {2719 if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2720 return Err(InvalidTransaction::Call.into());2721 }2722 }27232724 let mut sponsor_transfer = false;2725 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2726 let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2727 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2728 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2729 let limit_time = last_tx_block + rate_limit;27302731 if block_number >= limit_time {2732 <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2733 sponsor_transfer = true;2734 }2735 } else {2736 sponsor_transfer = false;2737 }2738 2739 2740 let mut sp = T::AccountId::default();2741 if sponsor_transfer {2742 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2743 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2744 sp = called_contract;2745 }2746 }2747 }27482749 sp2750 },27512752 _ => sponsor,2753 };27542755 let mut who_pays_fee: T::AccountId = sponsor.clone();2756 if sponsor == T::AccountId::default() {2757 who_pays_fee = who.clone();2758 }27592760 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2761 .map(|i| (fee, i))2762 }2763}276427652766impl<T: Config + Send + Sync> SignedExtension2767 for ChargeTransactionPayment<T>2768where2769 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2770 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2771 T::AccountId: AsRef<[u8]>,2772 T::AccountId: UncheckedFrom<T::Hash>,2773{2774 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2775 type AccountId = T::AccountId;2776 type Call = T::Call;2777 type AdditionalSigned = ();2778 type Pre = (2779 // tip2780 BalanceOf<T>,2781 // who pays fee2782 Self::AccountId,2783 // imbalance resulting from withdrawing the fee2784 <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2785 );2786 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2787 Ok(())2788 }27892790 fn validate(2791 &self,2792 who: &Self::AccountId,2793 call: &Self::Call,2794 info: &DispatchInfoOf<Self::Call>,2795 len: usize,2796 ) -> TransactionValidity {2797 let (fee, _) = self.withdraw_fee(who, call, info, len)?;2798 Ok(ValidTransaction {2799 priority: Self::get_priority(len, info, fee),2800 ..Default::default()2801 })2802 }28032804 fn pre_dispatch(2805 self,2806 who: &Self::AccountId,2807 call: &Self::Call,2808 info: &DispatchInfoOf<Self::Call>,2809 len: usize,2810 ) -> Result<Self::Pre, TransactionValidityError> {2811 let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2812 Ok((self.0, who.clone(), imbalance))2813 }28142815 fn post_dispatch(2816 pre: Self::Pre,2817 info: &DispatchInfoOf<Self::Call>,2818 post_info: &PostDispatchInfoOf<Self::Call>,2819 len: usize,2820 _result: &DispatchResult,2821 ) -> Result<(), TransactionValidityError> {2822 let (tip, who, imbalance) = pre;2823 let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2824 len as u32,2825 info,2826 post_info,2827 tip,2828 );2829 <T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2830 Ok(())2831 }2832}28332834// #endregion