1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage, decl_error,12 dispatch::DispatchResult,13 ensure, fail, parameter_types,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29 traits::{30 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31 },32 transaction_validity::{33 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34 },35 FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748495051pub type CollectionId = u32;52pub type TokenId = u32;5354#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]55#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]56pub enum CollectionMode {57 Invalid,58 NFT,59 60 Fungible(u32),61 62 ReFungible(u32),63}6465impl Into<u8> for CollectionMode {66 fn into(self) -> u8 {67 match self {68 CollectionMode::Invalid => 0,69 CollectionMode::NFT => 1,70 CollectionMode::Fungible(_) => 2,71 CollectionMode::ReFungible(_) => 3,72 }73 }74}7576#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]77#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]78pub enum AccessMode {79 Normal,80 WhiteList,81}82impl Default for AccessMode {83 fn default() -> Self {84 Self::Normal85 }86}8788impl Default for CollectionMode {89 fn default() -> Self {90 Self::Invalid91 }92}9394#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub struct Ownership<AccountId> {97 pub owner: AccountId,98 pub fraction: u128,99}100101#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub struct CollectionType<AccountId> {104 pub owner: AccountId,105 pub mode: CollectionMode,106 pub access: AccessMode,107 pub decimal_points: u32,108 pub name: Vec<u16>, 109 pub description: Vec<u16>, 110 pub token_prefix: Vec<u8>, 111 pub mint_mode: bool,112 pub offchain_schema: Vec<u8>,113 pub sponsor: AccountId, 114 pub unconfirmed_sponsor: AccountId, 115 pub variable_on_chain_schema: Vec<u8>, 116 pub const_on_chain_schema: Vec<u8>, 117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct NftItemType<AccountId> {122 pub collection: CollectionId,123 pub owner: AccountId,124 pub const_data: Vec<u8>,125 pub variable_data: Vec<u8>,126}127128#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]129#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]130pub struct FungibleItemType<AccountId> {131 pub collection: CollectionId,132 pub owner: AccountId,133 pub value: u128,134}135136#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]137#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]138pub struct ReFungibleItemType<AccountId> {139 pub collection: CollectionId,140 pub owner: Vec<Ownership<AccountId>>,141 pub const_data: Vec<u8>,142 pub variable_data: Vec<u8>,143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct ApprovePermissions<AccountId> {148 pub approved: AccountId,149 pub amount: u64,150}151152#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154pub struct VestingItem<AccountId, Moment> {155 pub sender: AccountId,156 pub recipient: AccountId,157 pub collection_id: CollectionId,158 pub item_id: TokenId,159 pub amount: u64,160 pub vesting_date: Moment,161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct BasketItem<AccountId, BlockNumber> {166 pub address: AccountId,167 pub start_block: BlockNumber,168}169170#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct ChainLimits {173 pub collection_numbers_limit: u32,174 pub account_token_ownership_limit: u32,175 pub collections_admins_limit: u64,176 pub custom_data_limit: u32,177178 179 pub nft_sponsor_transfer_timeout: u32,180 pub fungible_sponsor_transfer_timeout: u32,181 pub refungible_sponsor_transfer_timeout: u32,182}183184pub trait WeightInfo {185 fn create_collection() -> Weight;186 fn destroy_collection() -> Weight;187 fn add_to_white_list() -> Weight;188 fn remove_from_white_list() -> Weight;189 fn set_public_access_mode() -> Weight;190 fn set_mint_permission() -> Weight;191 fn change_collection_owner() -> Weight;192 fn add_collection_admin() -> Weight;193 fn remove_collection_admin() -> Weight;194 fn set_collection_sponsor() -> Weight;195 fn confirm_sponsorship() -> Weight;196 fn remove_collection_sponsor() -> Weight;197 fn create_item(s: usize) -> Weight;198 fn burn_item() -> Weight;199 fn transfer() -> Weight;200 fn approve() -> Weight;201 fn transfer_from() -> Weight;202 fn set_offchain_schema() -> Weight;203 fn set_const_on_chain_schema() -> Weight;204 fn set_variable_on_chain_schema() -> Weight;205 fn set_variable_meta_data() -> Weight;206 fn enable_contract_sponsoring() -> Weight;207}208209#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]210#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]211pub struct CreateNftData {212 pub const_data: Vec<u8>,213 pub variable_data: Vec<u8>,214}215216#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]217#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]218pub struct CreateFungibleData {219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateReFungibleData {224 pub const_data: Vec<u8>,225 pub variable_data: Vec<u8>,226}227228#[derive(Encode, Decode, Debug, Clone, PartialEq)]229#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]230pub enum CreateItemData {231 NFT(CreateNftData),232 Fungible(CreateFungibleData),233 ReFungible(CreateReFungibleData)234}235236impl CreateItemData {237 pub fn len(&self) -> usize {238 let len = match self {239 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),240 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),241 _ => 0242 };243 244 return len;245 }246}247248impl From<CreateNftData> for CreateItemData {249 fn from(item: CreateNftData) -> Self {250 CreateItemData::NFT(item)251 }252}253254impl From<CreateReFungibleData> for CreateItemData {255 fn from(item: CreateReFungibleData) -> Self {256 CreateItemData::ReFungible(item)257 }258}259260impl From<CreateFungibleData> for CreateItemData {261 fn from(item: CreateFungibleData) -> Self {262 CreateItemData::Fungible(item)263 }264}265266267decl_error! {268 269 pub enum Error for Module<T: Trait> {270 271 TotalCollectionsLimitExceeded,272 273 CollectionDecimalPointLimitExceeded, 274 275 CollectionNameLimitExceeded, 276 277 CollectionDescriptionLimitExceeded, 278 279 CollectionTokenPrefixLimitExceeded,280 281 CollectionNotFound,282 283 TokenNotFound,284 285 NumOverflow, 286 287 AlreadyAdmin, 288 289 NoPermission,290 291 ConfirmUnsetSponsorFail,292 293 PublicMintingNotAllowed,294 295 MustBeTokenOwner,296 297 TokenValueTooLow,298 299 NftSizeLimitExceeded,300 301 ApproveNotFound,302 303 TokenValueNotEnough,304 305 ApproveRequired,306 307 AddresNotInWhiteList,308 309 CollectionAdminsLimitExceeded,310 311 AddressOwnershipLimitExceeded,312 313 EmptyArgument,314 315 TokenConstDataLimitExceeded,316 317 TokenVariableDataLimitExceeded,318 319 NotNftDataUsedToMintNftCollectionToken,320 321 NotFungibleDataUsedToMintFungibleCollectionToken,322 323 NotReFungibleDataUsedToMintReFungibleCollectionToken,324 325 UnexpectedCollectionType,326 327 CantStoreMetadataInFungibleTokens328 }329}330331pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {332 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;333334 335 type WeightInfo: WeightInfo;336}337338#[cfg(feature = "runtime-benchmarks")]339mod benchmarking;340341342343decl_storage! {344 trait Store for Module<T: Trait> as Nft {345346 347 NextCollectionID: CollectionId;348 CreatedCollectionCount: u32;349 ChainVersion: u64;350 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;351352 353 pub ChainLimit get(fn chain_limit) config(): ChainLimits;354355 356 CollectionCount: u32;357 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u32;358359 360 pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;361 pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;362 pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;363364 365 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => u64;366367 368 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;369370 371 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;372 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => FungibleItemType<T::AccountId>;373 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;374375 376 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;377378 379 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;380 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;381 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;382383 384 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;385 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;386 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;387 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;388 }389 add_extra_genesis {390 build(|config: &GenesisConfig<T>| {391 392 for (_num, _c) in &config.collection {393 <Module<T>>::init_collection(_c);394 }395396 for (_num, _q, _i) in &config.nft_item_id {397 <Module<T>>::init_nft_token(_i);398 }399400 for (_num, _q, _i) in &config.fungible_item_id {401 <Module<T>>::init_fungible_token(_i);402 }403404 for (_num, _q, _i) in &config.refungible_item_id {405 <Module<T>>::init_refungible_token(_i);406 }407 })408 }409}410411decl_event!(412 pub enum Event<T>413 where414 AccountId = <T as system::Trait>::AccountId,415 {416 417 418 419 420 421 422 423 424 425 Created(CollectionId, u8, AccountId),426427 428 429 430 431 432 433 434 ItemCreated(CollectionId, TokenId),435436 437 438 439 440 441 442 443 ItemDestroyed(CollectionId, TokenId),444 }445);446447decl_module! {448 pub struct Module<T: Trait> for enum Call where origin: T::Origin {449450 fn deposit_event() = default;451 type Error = Error<T>;452453 fn on_initialize(now: T::BlockNumber) -> Weight {454455 if ChainVersion::get() < 2456 {457 let value = NextCollectionID::get();458 CreatedCollectionCount::put(value);459 ChainVersion::put(2);460 }461462 0463 }464465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 #[weight = T::WeightInfo::create_collection()]482 pub fn create_collection(origin,483 collection_name: Vec<u16>,484 collection_description: Vec<u16>,485 token_prefix: Vec<u8>,486 mode: CollectionMode) -> DispatchResult {487488 489 let who = ensure_signed(origin)?;490491 let decimal_points = match mode {492 CollectionMode::Fungible(points) => points,493 CollectionMode::ReFungible(points) => points,494 _ => 0495 };496497 498 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);499500 501 ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);502503 let mut name = collection_name.to_vec();504 name.push(0);505 ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);506507 let mut description = collection_description.to_vec();508 description.push(0);509 ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);510511 let mut prefix = token_prefix.to_vec();512 prefix.push(0);513 ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);514515 516 let next_id = CreatedCollectionCount::get()517 .checked_add(1)518 .ok_or(Error::<T>::NumOverflow)?;519520 521 let total = CollectionCount::get()522 .checked_add(1)523 .ok_or(Error::<T>::NumOverflow)?;524525 CreatedCollectionCount::put(next_id);526 CollectionCount::put(total);527528 529 let new_collection = CollectionType {530 owner: who.clone(),531 name: name,532 mode: mode.clone(),533 mint_mode: false,534 access: AccessMode::Normal,535 description: description,536 decimal_points: decimal_points,537 token_prefix: prefix,538 offchain_schema: Vec::new(),539 sponsor: T::AccountId::default(),540 unconfirmed_sponsor: T::AccountId::default(),541 variable_on_chain_schema: Vec::new(),542 const_on_chain_schema: Vec::new(),543 };544545 546 <Collection<T>>::insert(next_id, new_collection);547548 549 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));550551 Ok(())552 }553554 555 556 557 558 559 560 561 562 563 #[weight = T::WeightInfo::destroy_collection()]564 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {565566 let sender = ensure_signed(origin)?;567 Self::check_owner_permissions(collection_id, sender)?;568569 <AddressTokens<T>>::remove_prefix(collection_id);570 <ApprovedList<T>>::remove_prefix(collection_id);571 <Balance<T>>::remove_prefix(collection_id);572 <ItemListIndex>::remove(collection_id);573 <AdminList<T>>::remove(collection_id);574 <Collection<T>>::remove(collection_id);575 <WhiteList<T>>::remove(collection_id);576577 <NftItemList<T>>::remove_prefix(collection_id);578 <FungibleItemList<T>>::remove_prefix(collection_id);579 <ReFungibleItemList<T>>::remove_prefix(collection_id);580581 <NftTransferBasket<T>>::remove_prefix(collection_id);582 <FungibleTransferBasket<T>>::remove_prefix(collection_id);583 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);584585 if CollectionCount::get() > 0586 {587 588 let total = CollectionCount::get()589 .checked_sub(1)590 .ok_or(Error::<T>::NumOverflow)?;591592 CollectionCount::put(total);593 }594595 Ok(())596 }597598 599 600 601 602 603 604 605 606 607 608 609 610 #[weight = T::WeightInfo::add_to_white_list()]611 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{612613 let sender = ensure_signed(origin)?;614 Self::check_owner_or_admin_permissions(collection_id, sender)?;615616 let mut white_list_collection: Vec<T::AccountId>;617 if <WhiteList<T>>::contains_key(collection_id) {618 white_list_collection = <WhiteList<T>>::get(collection_id);619 if !white_list_collection.contains(&address.clone())620 {621 white_list_collection.push(address.clone());622 }623 }624 else {625 white_list_collection = Vec::new();626 white_list_collection.push(address.clone());627 }628629 <WhiteList<T>>::insert(collection_id, white_list_collection);630 Ok(())631 }632633 634 635 636 637 638 639 640 641 642 643 644 645 #[weight = T::WeightInfo::remove_from_white_list()]646 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{647648 let sender = ensure_signed(origin)?;649 Self::check_owner_or_admin_permissions(collection_id, sender)?;650651 if <WhiteList<T>>::contains_key(collection_id) {652 let mut white_list_collection = <WhiteList<T>>::get(collection_id);653 if white_list_collection.contains(&address.clone())654 {655 white_list_collection.retain(|i| *i != address.clone());656 <WhiteList<T>>::insert(collection_id, white_list_collection);657 }658 }659660 Ok(())661 }662663 664 665 666 667 668 669 670 671 672 673 674 #[weight = T::WeightInfo::set_public_access_mode()]675 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult676 {677 let sender = ensure_signed(origin)?;678679 Self::check_owner_permissions(collection_id, sender)?;680 let mut target_collection = <Collection<T>>::get(collection_id);681 target_collection.access = mode;682 <Collection<T>>::insert(collection_id, target_collection);683684 Ok(())685 }686687 688 689 690 691 692 693 694 695 696 697 698 699 700 #[weight = T::WeightInfo::set_mint_permission()]701 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult702 {703 let sender = ensure_signed(origin)?;704705 Self::check_owner_permissions(collection_id, sender)?;706 let mut target_collection = <Collection<T>>::get(collection_id);707 target_collection.mint_mode = mint_permission;708 <Collection<T>>::insert(collection_id, target_collection);709710 Ok(())711 }712713 714 715 716 717 718 719 720 721 722 723 724 #[weight = T::WeightInfo::change_collection_owner()]725 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {726727 let sender = ensure_signed(origin)?;728 Self::check_owner_permissions(collection_id, sender)?;729 let mut target_collection = <Collection<T>>::get(collection_id);730 target_collection.owner = new_owner;731 <Collection<T>>::insert(collection_id, target_collection);732733 Ok(())734 }735736 737 738 739 740 741 742 743 744 745 746 747 748 749 #[weight = T::WeightInfo::add_collection_admin()]750 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {751752 let sender = ensure_signed(origin)?;753 Self::check_owner_or_admin_permissions(collection_id, sender)?;754 let mut admin_arr: Vec<T::AccountId> = Vec::new();755756 if <AdminList<T>>::contains_key(collection_id)757 {758 admin_arr = <AdminList<T>>::get(collection_id);759 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);760 }761762 763 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);764765 admin_arr.push(new_admin_id);766 <AdminList<T>>::insert(collection_id, admin_arr);767768 Ok(())769 }770771 772 773 774 775 776 777 778 779 780 781 782 783 #[weight = T::WeightInfo::remove_collection_admin()]784 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {785786 let sender = ensure_signed(origin)?;787 Self::check_owner_or_admin_permissions(collection_id, sender)?;788789 if <AdminList<T>>::contains_key(collection_id)790 {791 let mut admin_arr = <AdminList<T>>::get(collection_id);792 admin_arr.retain(|i| *i != account_id);793 <AdminList<T>>::insert(collection_id, admin_arr);794 }795796 Ok(())797 }798799 800 801 802 803 804 805 806 807 808 #[weight = T::WeightInfo::set_collection_sponsor()]809 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {810811 let sender = ensure_signed(origin)?;812 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);813814 let mut target_collection = <Collection<T>>::get(collection_id);815 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);816817 target_collection.unconfirmed_sponsor = new_sponsor;818 <Collection<T>>::insert(collection_id, target_collection);819820 Ok(())821 }822823 824 825 826 827 828 829 830 #[weight = T::WeightInfo::confirm_sponsorship()]831 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {832833 let sender = ensure_signed(origin)?;834 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);835836 let mut target_collection = <Collection<T>>::get(collection_id);837 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);838839 target_collection.sponsor = target_collection.unconfirmed_sponsor;840 target_collection.unconfirmed_sponsor = T::AccountId::default();841 <Collection<T>>::insert(collection_id, target_collection);842843 Ok(())844 }845846 847 848 849 850 851 852 853 854 855 #[weight = T::WeightInfo::remove_collection_sponsor()]856 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {857858 let sender = ensure_signed(origin)?;859 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);860861 let mut target_collection = <Collection<T>>::get(collection_id);862 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);863864 target_collection.sponsor = T::AccountId::default();865 <Collection<T>>::insert(collection_id, target_collection);866867 Ok(())868 }869870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893894 #[weight = T::WeightInfo::create_item(data.len())]895 pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {896897 let sender = ensure_signed(origin)?;898899 Self::collection_exists(collection_id)?;900901 let target_collection = <Collection<T>>::get(collection_id);902903 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;904 Self::validate_create_item_args(&target_collection, &data)?;905 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;906907 Ok(())908 }909910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 #[weight = T::WeightInfo::create_item(items_data.into_iter()929 .map(|data| { data.len() })930 .sum())]931 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {932933 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);934 let sender = ensure_signed(origin)?;935936 Self::collection_exists(collection_id)?;937 let target_collection = <Collection<T>>::get(collection_id);938939 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;940941 for data in &items_data {942 Self::validate_create_item_args(&target_collection, data)?;943 }944 for data in &items_data {945 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;946 }947948 Ok(())949 }950951 952 953 954 955 956 957 958 959 960 961 962 963 964 #[weight = T::WeightInfo::burn_item()]965 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {966967 let sender = ensure_signed(origin)?;968 Self::collection_exists(collection_id)?;969970 971 let target_collection = <Collection<T>>::get(collection_id);972 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||973 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),974 Error::<T>::NoPermission);975976 if target_collection.access == AccessMode::WhiteList {977 Self::check_white_list(collection_id, &sender)?;978 }979980 match target_collection.mode981 {982 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,983 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,984 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,985 _ => ()986 };987988 989 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));990991 Ok(())992 }993994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 #[weight = T::WeightInfo::transfer()]1018 pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64) -> DispatchResult {10191020 let sender = ensure_signed(origin)?;10211022 1023 let target_collection = <Collection<T>>::get(collection_id);1024 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1025 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1026 Error::<T>::NoPermission);10271028 if target_collection.access == AccessMode::WhiteList {1029 Self::check_white_list(collection_id, &sender)?;1030 Self::check_white_list(collection_id, &recipient)?;1031 }10321033 match target_collection.mode1034 {1035 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1036 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1037 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1038 _ => ()1039 };10401041 Ok(())1042 }10431044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 #[weight = T::WeightInfo::approve()]1060 pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10611062 let sender = ensure_signed(origin)?;10631064 1065 let target_collection = <Collection<T>>::get(collection_id);1066 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1067 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1068 Error::<T>::NoPermission);10691070 if target_collection.access == AccessMode::WhiteList {1071 Self::check_white_list(collection_id, &sender)?;1072 Self::check_white_list(collection_id, &approved)?;1073 }10741075 1076 let amount = 100000000;10771078 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1079 if list_exists {10801081 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1082 let item_contains = list.iter().any(|i| i.approved == approved);10831084 if !item_contains {1085 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1086 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1087 }1088 } else {10891090 let mut list = Vec::new();1091 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1092 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1093 }10941095 Ok(())1096 }1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 #[weight = T::WeightInfo::transfer_from()]1118 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64 ) -> DispatchResult {11191120 let sender = ensure_signed(origin)?;1121 let mut appoved_transfer = false;11221123 1124 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1125 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1126 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1127 if opt_item.is_some()1128 {1129 appoved_transfer = true;1130 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1131 }1132 }11331134 1135 let target_collection = <Collection<T>>::get(collection_id);1136 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1137 Error::<T>::NoPermission);11381139 if target_collection.access == AccessMode::WhiteList {1140 Self::check_white_list(collection_id, &sender)?;1141 Self::check_white_list(collection_id, &recipient)?;1142 }11431144 1145 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1146 .into_iter().filter(|i| i.approved != sender.clone()).collect();1147 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114811491150 match target_collection.mode1151 {1152 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1153 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1154 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1155 _ => ()1156 };11571158 Ok(())1159 }11601161 1162 #[weight = 0]1163 pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11641165 1166 1167 1168 11691170 11711172 11731174 Ok(())1175 }11761177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 #[weight = T::WeightInfo::set_variable_meta_data()]1190 pub fn set_variable_meta_data (1191 origin,1192 collection_id: CollectionId,1193 item_id: TokenId,1194 data: Vec<u8>1195 ) -> DispatchResult {1196 let sender = ensure_signed(origin)?;1197 1198 Self::collection_exists(collection_id)?;1199 1200 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12011202 1203 let target_collection = <Collection<T>>::get(collection_id);1204 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1205 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1206 Error::<T>::NoPermission);12071208 Self::item_exists(collection_id, item_id, &target_collection.mode)?;12091210 match target_collection.mode1211 {1212 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1213 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1214 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1215 _ => fail!(Error::<T>::UnexpectedCollectionType)1216 };12171218 Ok(())1219 }1220 12211222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 #[weight = T::WeightInfo::set_offchain_schema()]1235 pub fn set_offchain_schema(1236 origin,1237 collection_id: CollectionId,1238 schema: Vec<u8>1239 ) -> DispatchResult {1240 let sender = ensure_signed(origin)?;1241 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12421243 let mut target_collection = <Collection<T>>::get(collection_id);1244 target_collection.offchain_schema = schema;1245 <Collection<T>>::insert(collection_id, target_collection);12461247 Ok(())1248 }12491250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 #[weight = T::WeightInfo::set_const_on_chain_schema()]1263 pub fn set_const_on_chain_schema (1264 origin,1265 collection_id: CollectionId,1266 schema: Vec<u8>1267 ) -> DispatchResult {1268 let sender = ensure_signed(origin)?;1269 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12701271 let mut target_collection = <Collection<T>>::get(collection_id);1272 target_collection.const_on_chain_schema = schema;1273 <Collection<T>>::insert(collection_id, target_collection);12741275 Ok(())1276 }12771278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 #[weight = T::WeightInfo::set_const_on_chain_schema()]1291 pub fn set_variable_on_chain_schema (1292 origin,1293 collection_id: CollectionId,1294 schema: Vec<u8>1295 ) -> DispatchResult {1296 let sender = ensure_signed(origin)?;1297 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12981299 let mut target_collection = <Collection<T>>::get(collection_id);1300 target_collection.variable_on_chain_schema = schema;1301 <Collection<T>>::insert(collection_id, target_collection);13021303 Ok(())1304 }13051306 1307 #[weight = 0]1308 pub fn set_chain_limits(1309 origin,1310 limits: ChainLimits1311 ) -> DispatchResult {1312 ensure_root(origin)?;1313 <ChainLimit>::put(limits);1314 Ok(())1315 }13161317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 #[weight = T::WeightInfo::enable_contract_sponsoring()]1329 pub fn enable_contract_sponsoring(1330 origin,1331 contract_address: T::AccountId,1332 enable: bool1333 ) -> DispatchResult {13341335 let sender = ensure_signed(origin)?;13361337 #[cfg(feature = "runtime-benchmarks")]1338 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13391340 let mut is_owner = false;1341 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1342 let owner = <ContractOwner<T>>::get(&contract_address);1343 is_owner = sender == owner;1344 }1345 ensure!(is_owner, Error::<T>::NoPermission);13461347 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1348 Ok(())1349 }13501351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 #[weight = 0]1369 pub fn set_contract_sponsoring_rate_limit(1370 origin,1371 contract_address: T::AccountId,1372 rate_limit: T::BlockNumber1373 ) -> DispatchResult {1374 let sender = ensure_signed(origin)?;1375 let mut is_owner = false;1376 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1377 let owner = <ContractOwner<T>>::get(&contract_address);1378 is_owner = sender == owner;1379 }1380 ensure!(is_owner, Error::<T>::NoPermission);13811382 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1383 Ok(())1384 }13851386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 }1398}13991400impl<T: Trait> Module<T> {14011402 fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14031404 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1405 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1406 Self::check_white_list(collection_id, owner)?;1407 Self::check_white_list(collection_id, sender)?;1408 }14091410 Ok(())1411 }14121413 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1414 match target_collection.mode1415 {1416 CollectionMode::NFT => {1417 if let CreateItemData::NFT(data) = data {1418 1419 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1420 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1421 } else {1422 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1423 }1424 },1425 CollectionMode::Fungible(_) => {1426 if let CreateItemData::Fungible(_) = data {1427 } else {1428 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1429 }1430 },1431 CollectionMode::ReFungible(_) => {1432 if let CreateItemData::ReFungible(data) = data {14331434 1435 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1436 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1437 } else {1438 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1439 }1440 },1441 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1442 };14431444 Ok(())1445 }14461447 fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1448 match data1449 {1450 CreateItemData::NFT(data) => {1451 let item = NftItemType {1452 collection: collection_id,1453 owner,1454 const_data: data.const_data,1455 variable_data: data.variable_data1456 };14571458 Self::add_nft_item(item)?;1459 },1460 CreateItemData::Fungible(_) => {1461 let item = FungibleItemType {1462 collection: collection_id,1463 owner,1464 value: (10 as u128).pow(collection.decimal_points)1465 };14661467 Self::add_fungible_item(item)?;1468 },1469 CreateItemData::ReFungible(data) => {1470 let mut owner_list = Vec::new();1471 let value = (10 as u128).pow(collection.decimal_points);1472 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14731474 let item = ReFungibleItemType {1475 collection: collection_id,1476 owner: owner_list,1477 const_data: data.const_data,1478 variable_data: data.variable_data1479 };14801481 Self::add_refungible_item(item)?;1482 }1483 };148414851486 1487 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14881489 Ok(())1490 }14911492 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1493 let current_index = <ItemListIndex>::get(item.collection)1494 .checked_add(1)1495 .ok_or(Error::<T>::NumOverflow)?;1496 let itemcopy = item.clone();1497 let owner = item.owner.clone();1498 let value = item.value as u64;14991500 Self::add_token_index(item.collection, current_index, owner.clone())?;15011502 <ItemListIndex>::insert(item.collection, current_index);1503 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15041505 1506 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1507 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1508 1509 1510 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1511 .checked_add(value)1512 .ok_or(Error::<T>::NumOverflow)?;1513 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15141515 Ok(())1516 }15171518 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1519 let current_index = <ItemListIndex>::get(item.collection)1520 .checked_add(1)1521 .ok_or(Error::<T>::NumOverflow)?;1522 let itemcopy = item.clone();15231524 let value = item.owner.first().unwrap().fraction as u64;1525 let owner = item.owner.first().unwrap().owner.clone();15261527 Self::add_token_index(item.collection, current_index, owner.clone())?;15281529 <ItemListIndex>::insert(item.collection, current_index);1530 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15311532 1533 let block_number: T::BlockNumber = 0.into();1534 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15351536 1537 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1538 .checked_add(value)1539 .ok_or(Error::<T>::NumOverflow)?;1540 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15411542 Ok(())1543 }15441545 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1546 let current_index = <ItemListIndex>::get(item.collection)1547 .checked_add(1)1548 .ok_or(Error::<T>::NumOverflow)?;15491550 let item_owner = item.owner.clone();1551 let collection_id = item.collection.clone();1552 Self::add_token_index(collection_id, current_index, item.owner.clone())?;15531554 <ItemListIndex>::insert(collection_id, current_index);1555 <NftItemList<T>>::insert(collection_id, current_index, item);15561557 1558 let block_number: T::BlockNumber = 0.into();1559 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15601561 1562 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1563 .checked_add(1)1564 .ok_or(Error::<T>::NumOverflow)?;1565 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15661567 Ok(())1568 }15691570 fn burn_refungible_item(1571 collection_id: CollectionId,1572 item_id: TokenId,1573 owner: T::AccountId,1574 ) -> DispatchResult {1575 ensure!(1576 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1577 Error::<T>::TokenNotFound1578 );1579 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1580 let item = collection1581 .owner1582 .iter()1583 .filter(|&i| i.owner == owner)1584 .next()1585 .unwrap();1586 Self::remove_token_index(collection_id, item_id, owner.clone())?;15871588 1589 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15901591 1592 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1593 .checked_sub(item.fraction as u64)1594 .ok_or(Error::<T>::NumOverflow)?;1595 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15961597 <ReFungibleItemList<T>>::remove(collection_id, item_id);15981599 Ok(())1600 }16011602 fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1603 ensure!(1604 <NftItemList<T>>::contains_key(collection_id, item_id),1605 Error::<T>::TokenNotFound1606 );1607 let item = <NftItemList<T>>::get(collection_id, item_id);1608 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16091610 1611 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16121613 1614 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1615 .checked_sub(1)1616 .ok_or(Error::<T>::NumOverflow)?;1617 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1618 <NftItemList<T>>::remove(collection_id, item_id);16191620 Ok(())1621 }16221623 fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1624 ensure!(1625 <FungibleItemList<T>>::contains_key(collection_id, item_id),1626 Error::<T>::TokenNotFound1627 );1628 let item = <FungibleItemList<T>>::get(collection_id, item_id);1629 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16301631 1632 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16331634 1635 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1636 .checked_sub(item.value as u64)1637 .ok_or(Error::<T>::NumOverflow)?;1638 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16391640 <FungibleItemList<T>>::remove(collection_id, item_id);16411642 Ok(())1643 }16441645 fn collection_exists(collection_id: CollectionId) -> DispatchResult {1646 ensure!(1647 <Collection<T>>::contains_key(collection_id),1648 Error::<T>::CollectionNotFound1649 );1650 Ok(())1651 }16521653 fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1654 Self::collection_exists(collection_id)?;16551656 let target_collection = <Collection<T>>::get(collection_id);1657 ensure!(1658 subject == target_collection.owner,1659 Error::<T>::NoPermission1660 );16611662 Ok(())1663 }16641665 fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1666 let target_collection = <Collection<T>>::get(collection_id);1667 let mut result: bool = subject == target_collection.owner;1668 let exists = <AdminList<T>>::contains_key(collection_id);16691670 if !result & exists {1671 if <AdminList<T>>::get(collection_id).contains(&subject) {1672 result = true1673 }1674 }16751676 result1677 }16781679 fn check_owner_or_admin_permissions(1680 collection_id: CollectionId,1681 subject: T::AccountId,1682 ) -> DispatchResult {1683 Self::collection_exists(collection_id)?;1684 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16851686 ensure!(1687 result,1688 Error::<T>::NoPermission1689 );1690 Ok(())1691 }16921693 fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1694 let target_collection = <Collection<T>>::get(collection_id);16951696 match target_collection.mode {1697 CollectionMode::NFT => {1698 <NftItemList<T>>::get(collection_id, item_id).owner == subject1699 }1700 CollectionMode::Fungible(_) => {1701 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1702 }1703 CollectionMode::ReFungible(_) => {1704 <ReFungibleItemList<T>>::get(collection_id, item_id)1705 .owner1706 .iter()1707 .any(|i| i.owner == subject)1708 }1709 CollectionMode::Invalid => false,1710 }1711 }17121713 fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1714 let mes = Error::<T>::AddresNotInWhiteList;1715 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1716 let wl = <WhiteList<T>>::get(collection_id);1717 ensure!(wl.contains(address), mes);17181719 Ok(())1720 }17211722 fn transfer_fungible(1723 collection_id: CollectionId,1724 item_id: TokenId,1725 value: u64,1726 owner: T::AccountId,1727 new_owner: T::AccountId,1728 ) -> DispatchResult {1729 ensure!(1730 <FungibleItemList<T>>::contains_key(collection_id, item_id),1731 Error::<T>::TokenNotFound1732 );17331734 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1735 let amount = full_item.value;17361737 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17381739 1740 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1741 .checked_sub(value)1742 .ok_or(Error::<T>::NumOverflow)?;1743 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17441745 let mut new_owner_account_id = 0;1746 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1747 if new_owner_items.len() > 0 {1748 new_owner_account_id = new_owner_items[0];1749 }17501751 let val64 = value.into();17521753 1754 if amount == val64 && new_owner_account_id == 0 {1755 1756 1757 let mut new_full_item = full_item.clone();1758 new_full_item.owner = new_owner.clone();1759 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17601761 1762 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1763 .checked_add(value)1764 .ok_or(Error::<T>::NumOverflow)?;1765 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17661767 1768 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1769 } else {1770 let mut new_full_item = full_item.clone();1771 new_full_item.value -= val64;17721773 1774 if new_owner_account_id > 0 {1775 1776 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1777 item.value += val64;17781779 1780 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1781 .checked_add(value)1782 .ok_or(Error::<T>::NumOverflow)?;1783 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17841785 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1786 } else {1787 1788 let item = FungibleItemType {1789 collection: collection_id,1790 owner: new_owner.clone(),1791 value: val64,1792 };17931794 Self::add_fungible_item(item)?;1795 }17961797 if amount == val64 {1798 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17991800 1801 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1802 <FungibleItemList<T>>::remove(collection_id, item_id);1803 }18041805 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1806 }18071808 Ok(())1809 }18101811 fn transfer_refungible(1812 collection_id: CollectionId,1813 item_id: TokenId,1814 value: u64,1815 owner: T::AccountId,1816 new_owner: T::AccountId,1817 ) -> DispatchResult {1818 ensure!(1819 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1820 Error::<T>::TokenNotFound1821 );18221823 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1824 let item = full_item1825 .owner1826 .iter()1827 .filter(|i| i.owner == owner)1828 .next()1829 .ok_or(Error::<T>::NumOverflow)?;1830 let amount = item.fraction;18311832 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18331834 1835 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1836 .checked_sub(value)1837 .ok_or(Error::<T>::NumOverflow)?;1838 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18391840 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1841 .checked_add(value)1842 .ok_or(Error::<T>::NumOverflow)?;1843 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18441845 let old_owner = item.owner.clone();1846 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1847 let val64 = value.into();18481849 1850 if amount == val64 && !new_owner_has_account {1851 1852 1853 let mut new_full_item = full_item.clone();1854 new_full_item1855 .owner1856 .iter_mut()1857 .find(|i| i.owner == owner)1858 .unwrap()1859 .owner = new_owner.clone();1860 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18611862 1863 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1864 } else {1865 let mut new_full_item = full_item.clone();1866 new_full_item1867 .owner1868 .iter_mut()1869 .find(|i| i.owner == owner)1870 .unwrap()1871 .fraction -= val64;18721873 1874 if new_owner_has_account {1875 1876 new_full_item1877 .owner1878 .iter_mut()1879 .find(|i| i.owner == new_owner)1880 .unwrap()1881 .fraction += val64;1882 } else {1883 1884 new_full_item.owner.push(Ownership {1885 owner: new_owner.clone(),1886 fraction: val64,1887 });1888 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1889 }18901891 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1892 }18931894 Ok(())1895 }18961897 fn transfer_nft(1898 collection_id: CollectionId,1899 item_id: TokenId,1900 sender: T::AccountId,1901 new_owner: T::AccountId,1902 ) -> DispatchResult {1903 ensure!(1904 <NftItemList<T>>::contains_key(collection_id, item_id),1905 Error::<T>::TokenNotFound1906 );19071908 let mut item = <NftItemList<T>>::get(collection_id, item_id);19091910 ensure!(1911 sender == item.owner,1912 Error::<T>::MustBeTokenOwner1913 );19141915 1916 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1917 .checked_sub(1)1918 .ok_or(Error::<T>::NumOverflow)?;1919 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19201921 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1922 .checked_add(1)1923 .ok_or(Error::<T>::NumOverflow)?;1924 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19251926 1927 let old_owner = item.owner.clone();1928 item.owner = new_owner.clone();1929 <NftItemList<T>>::insert(collection_id, item_id, item);19301931 1932 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19331934 1935 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1936 Ok(())1937 }1938 1939 fn item_exists(1940 collection_id: CollectionId,1941 item_id: TokenId,1942 mode: &CollectionMode1943 ) -> DispatchResult {1944 match mode {1945 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1946 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1947 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1948 _ => ()1949 };1950 1951 Ok(())1952 }19531954 fn set_re_fungible_variable_data(1955 collection_id: CollectionId,1956 item_id: TokenId,1957 data: Vec<u8>1958 ) -> DispatchResult {1959 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19601961 item.variable_data = data;19621963 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19641965 Ok(())1966 }19671968 fn set_nft_variable_data(1969 collection_id: CollectionId,1970 item_id: TokenId,1971 data: Vec<u8>1972 ) -> DispatchResult {1973 let mut item = <NftItemList<T>>::get(collection_id, item_id);1974 1975 item.variable_data = data;19761977 <NftItemList<T>>::insert(collection_id, item_id, item);1978 1979 Ok(())1980 }19811982 fn init_collection(item: &CollectionType<T::AccountId>) {1983 1984 assert!(1985 item.decimal_points <= 4,1986 "decimal_points parameter must be lower than 4"1987 );1988 assert!(1989 item.name.len() <= 64,1990 "Collection name can not be longer than 63 char"1991 );1992 assert!(1993 item.name.len() <= 256,1994 "Collection description can not be longer than 255 char"1995 );1996 assert!(1997 item.token_prefix.len() <= 16,1998 "Token prefix can not be longer than 15 char"1999 );20002001 2002 let next_id = CreatedCollectionCount::get()2003 .checked_add(1)2004 .unwrap();20052006 CreatedCollectionCount::put(next_id);2007 }20082009 fn init_nft_token(item: &NftItemType<T::AccountId>) {2010 let current_index = <ItemListIndex>::get(item.collection)2011 .checked_add(1)2012 .unwrap();20132014 let item_owner = item.owner.clone();2015 let collection_id = item.collection.clone();2016 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20172018 <ItemListIndex>::insert(collection_id, current_index);20192020 2021 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2022 .checked_add(1)2023 .unwrap();2024 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2025 }20262027 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2028 let current_index = <ItemListIndex>::get(item.collection)2029 .checked_add(1)2030 .unwrap();2031 let owner = item.owner.clone();2032 let value = item.value as u64;20332034 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20352036 <ItemListIndex>::insert(item.collection, current_index);20372038 2039 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2040 .checked_add(value)2041 .unwrap();2042 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2043 }20442045 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2046 let current_index = <ItemListIndex>::get(item.collection)2047 .checked_add(1)2048 .unwrap();20492050 let value = item.owner.first().unwrap().fraction as u64;2051 let owner = item.owner.first().unwrap().owner.clone();20522053 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20542055 <ItemListIndex>::insert(item.collection, current_index);20562057 2058 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2059 .checked_add(value)2060 .unwrap();2061 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2062 }20632064 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {20652066 2067 if <AccountItemCount<T>>::contains_key(owner.clone()) {20682069 2070 let count = <AccountItemCount<T>>::get(owner.clone());2071 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20722073 <AccountItemCount<T>>::insert(owner.clone(), count2074 .checked_add(1)2075 .ok_or(Error::<T>::NumOverflow)?);2076 }2077 else {2078 <AccountItemCount<T>>::insert(owner.clone(), 1);2079 }20802081 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2082 if list_exists {2083 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2084 let item_contains = list.contains(&item_index.clone());20852086 if !item_contains {2087 list.push(item_index.clone());2088 }20892090 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2091 } else {2092 let mut itm = Vec::new();2093 itm.push(item_index.clone());2094 <AddressTokens<T>>::insert(collection_id, owner, itm);2095 2096 }20972098 Ok(())2099 }21002101 fn remove_token_index(2102 collection_id: CollectionId,2103 item_index: TokenId,2104 owner: T::AccountId,2105 ) -> DispatchResult {21062107 2108 <AccountItemCount<T>>::insert(owner.clone(), 2109 <AccountItemCount<T>>::get(owner.clone())2110 .checked_sub(1)2111 .ok_or(Error::<T>::NumOverflow)?);211221132114 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2115 if list_exists {2116 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2117 let item_contains = list.contains(&item_index.clone());21182119 if item_contains {2120 list.retain(|&item| item != item_index);2121 <AddressTokens<T>>::insert(collection_id, owner, list);2122 }2123 }21242125 Ok(())2126 }21272128 fn move_token_index(2129 collection_id: CollectionId,2130 item_index: TokenId,2131 old_owner: T::AccountId,2132 new_owner: T::AccountId,2133 ) -> DispatchResult {2134 Self::remove_token_index(collection_id, item_index, old_owner)?;2135 Self::add_token_index(collection_id, item_index, new_owner)?;21362137 Ok(())2138 }2139}2140214121422143214421452146pub type Multiplier = FixedU128;21472148type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2149 <T as system::Trait>::AccountId,2150>>::Balance;2151type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2152 <T as system::Trait>::AccountId,2153>>::NegativeImbalance;2154215521562157#[derive(Encode, Decode, Clone, Eq, PartialEq)]2158pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2159 #[codec(compact)] BalanceOf<T>2160);21612162impl<T: Trait + Send + Sync> sp_std::fmt::Debug2163 for ChargeTransactionPayment<T>2164{2165 #[cfg(feature = "std")]2166 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2167 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2168 }2169 #[cfg(not(feature = "std"))]2170 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2171 Ok(())2172 }2173}21742175impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2176where2177 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2178 BalanceOf<T>: Send + Sync + FixedPointOperand,2179{2180 2181 pub fn from(fee: BalanceOf<T>) -> Self {2182 Self(fee)2183 }21842185 pub fn traditional_fee(2186 len: usize,2187 info: &DispatchInfoOf<T::Call>,2188 tip: BalanceOf<T>,2189 ) -> BalanceOf<T>2190 where2191 T::Call: Dispatchable<Info = DispatchInfo>,2192 {2193 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2194 }21952196 fn withdraw_fee(2197 &self,2198 who: &T::AccountId,2199 call: &T::Call,2200 info: &DispatchInfoOf<T::Call>,2201 len: usize,2202 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2203 let tip = self.0;22042205 2206 2207 2208 2209 2210 2211 2212 let fee = Self::traditional_fee(len, info, tip);22132214 2215 2216 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2217 Some(Call::create_item(collection_id, _properties, _owner)) => {2218 <Collection<T>>::get(collection_id).sponsor2219 }2220 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2221 let _collection_mode = <Collection<T>>::get(collection_id).mode;22222223 2224 let sponsor_transfer = match _collection_mode {2225 CollectionMode::NFT => {2226 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2227 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2228 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2229 if block_number >= limit_time {2230 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2231 true2232 }2233 else {2234 false2235 }2236 }2237 CollectionMode::Fungible(_) => {2238 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2239 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2240 if basket.iter().any(|i| i.address == _new_owner.clone())2241 {2242 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2243 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2244 if block_number >= limit_time {2245 basket.retain(|x| x.address == item.address);2246 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2247 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2248 true2249 }2250 else {2251 false2252 }2253 }2254 else {2255 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2256 true2257 }2258 }2259 CollectionMode::ReFungible(_) => {2260 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2261 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2262 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2263 if block_number >= limit_time {2264 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2265 true2266 } else {2267 false2268 }2269 }2270 _ => {2271 false2272 },2273 };22742275 if !sponsor_transfer {2276 T::AccountId::default()2277 } else {2278 <Collection<T>>::get(collection_id).sponsor2279 }2280 }22812282 _ => T::AccountId::default(),2283 };22842285 2286 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22872288 2289 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22902291 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2292 code_hash,2293 &data,2294 &who,2295 );2296 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22972298 T::AccountId::default()2299 },23002301 2302 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23032304 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23052306 let mut sponsor_transfer = false;2307 if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2308 let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2309 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2310 let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2311 let limit_time = last_tx_block + rate_limit;23122313 if block_number >= limit_time {2314 <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2315 sponsor_transfer = true;2316 }2317 } else {2318 sponsor_transfer = false;2319 }2320 2321 2322 let mut sp = T::AccountId::default();2323 if sponsor_transfer {2324 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2325 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2326 sp = called_contract;2327 }2328 }2329 }23302331 sp2332 },23332334 _ => sponsor,2335 };23362337 let mut who_pays_fee: T::AccountId = sponsor.clone();2338 if sponsor == T::AccountId::default() {2339 who_pays_fee = who.clone();2340 }23412342 2343 if fee.is_zero() {2344 return Ok((fee, None));2345 }23462347 match <T as transaction_payment::Trait>::Currency::withdraw(2348 &who_pays_fee,2349 fee,2350 if tip.is_zero() {2351 WithdrawReason::TransactionPayment.into()2352 } else {2353 WithdrawReason::TransactionPayment | WithdrawReason::Tip2354 },2355 ExistenceRequirement::KeepAlive,2356 ) {2357 Ok(imbalance) => Ok((fee, Some(imbalance))),2358 Err(_) => Err(InvalidTransaction::Payment.into()),2359 }2360 }2361}236223632364impl<T: Trait + Send + Sync> SignedExtension2365 for ChargeTransactionPayment<T>2366where2367 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2368 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2369{2370 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2371 type AccountId = T::AccountId;2372 type Call = T::Call;2373 type AdditionalSigned = ();2374 type Pre = (2375 BalanceOf<T>,2376 Self::AccountId,2377 Option<NegativeImbalanceOf<T>>,2378 BalanceOf<T>,2379 );2380 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2381 Ok(())2382 }23832384 fn validate(2385 &self,2386 _who: &Self::AccountId,2387 _call: &Self::Call,2388 _info: &DispatchInfoOf<Self::Call>,2389 _len: usize,2390 ) -> TransactionValidity {2391 Ok(ValidTransaction::default())2392 }23932394 fn pre_dispatch(2395 self,2396 who: &Self::AccountId,2397 call: &Self::Call,2398 info: &DispatchInfoOf<Self::Call>,2399 len: usize,2400 ) -> Result<Self::Pre, TransactionValidityError> {2401 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2402 Ok((self.0, who.clone(), imbalance, fee))2403 }24042405 fn post_dispatch(2406 pre: Self::Pre,2407 info: &DispatchInfoOf<Self::Call>,2408 post_info: &PostDispatchInfoOf<Self::Call>,2409 len: usize,2410 _result: &DispatchResult,2411 ) -> Result<(), TransactionValidityError> {2412 let (tip, who, imbalance, fee) = pre;2413 if let Some(payed) = imbalance {2414 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2415 len as u32, info, post_info, tip,2416 );2417 let refund = fee.saturating_sub(actual_fee);2418 let actual_payment =2419 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2420 &who, refund,2421 ) {2422 Ok(refund_imbalance) => {2423 2424 2425 match payed.offset(refund_imbalance) {2426 Ok(actual_payment) => actual_payment,2427 Err(_) => return Err(InvalidTransaction::Payment.into()),2428 }2429 }2430 2431 2432 Err(_) => payed,2433 };2434 let imbalances = actual_payment.split(tip);2435 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2436 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2437 );2438 }2439 Ok(())2440 }2441}24422443