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;4748495051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54 Invalid,55 NFT,56 57 Fungible(u32),58 59 ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63 fn into(self) -> u8 {64 match self {65 CollectionMode::Invalid => 0,66 CollectionMode::NFT => 1,67 CollectionMode::Fungible(_) => 2,68 CollectionMode::ReFungible(_) => 3,69 }70 }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76 Normal,77 WhiteList,78}79impl Default for AccessMode {80 fn default() -> Self {81 Self::Normal82 }83}8485impl Default for CollectionMode {86 fn default() -> Self {87 Self::Invalid88 }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94 pub owner: AccountId,95 pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101 pub owner: AccountId,102 pub mode: CollectionMode,103 pub access: AccessMode,104 pub decimal_points: u32,105 pub name: Vec<u16>, 106 pub description: Vec<u16>, 107 pub token_prefix: Vec<u8>, 108 pub mint_mode: bool,109 pub offchain_schema: Vec<u8>,110 pub sponsor: AccountId, 111 pub unconfirmed_sponsor: AccountId, 112 pub variable_on_chain_schema: Vec<u8>, 113 pub const_on_chain_schema: Vec<u8>, 114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119 pub collection: u64,120 pub owner: AccountId,121 pub const_data: Vec<u8>,122 pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128 pub collection: u64,129 pub owner: AccountId,130 pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136 pub collection: u64,137 pub owner: Vec<Ownership<AccountId>>,138 pub const_data: Vec<u8>,139 pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145 pub approved: AccountId,146 pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152 pub sender: AccountId,153 pub recipient: AccountId,154 pub collection_id: u64,155 pub item_id: u64,156 pub amount: u64,157 pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163 pub address: AccountId,164 pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170 pub collection_numbers_limit: u64,171 pub account_token_ownership_limit: u64,172 pub collections_admins_limit: u64,173 pub custom_data_limit: u32,174175 176 pub nft_sponsor_transfer_timeout: u32,177 pub fungible_sponsor_transfer_timeout: u32,178 pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182 fn create_collection() -> Weight;183 fn destroy_collection() -> Weight;184 fn add_to_white_list() -> Weight;185 fn remove_from_white_list() -> Weight;186 fn set_public_access_mode() -> Weight;187 fn set_mint_permission() -> Weight;188 fn change_collection_owner() -> Weight;189 fn add_collection_admin() -> Weight;190 fn remove_collection_admin() -> Weight;191 fn set_collection_sponsor() -> Weight;192 fn confirm_sponsorship() -> Weight;193 fn remove_collection_sponsor() -> Weight;194 fn create_item(s: usize) -> Weight;195 fn burn_item() -> Weight;196 fn transfer() -> Weight;197 fn approve() -> Weight;198 fn transfer_from() -> Weight;199 fn set_offchain_schema() -> Weight;200 fn set_const_on_chain_schema() -> Weight;201 fn set_variable_on_chain_schema() -> Weight;202 fn set_variable_meta_data() -> Weight;203 204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209 pub const_data: Vec<u8>,210 pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221 pub const_data: Vec<u8>,222 pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228 NFT(CreateNftData),229 Fungible(CreateFungibleData),230 ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234 pub fn len(&self) -> usize {235 let len = match self {236 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238 _ => 0239 };240 241 return len;242 }243}244245impl From<CreateNftData> for CreateItemData {246 fn from(item: CreateNftData) -> Self {247 CreateItemData::NFT(item)248 }249}250251impl From<CreateReFungibleData> for CreateItemData {252 fn from(item: CreateReFungibleData) -> Self {253 CreateItemData::ReFungible(item)254 }255}256257impl From<CreateFungibleData> for CreateItemData {258 fn from(item: CreateFungibleData) -> Self {259 CreateItemData::Fungible(item)260 }261}262263264decl_error! {265 266 pub enum Error for Module<T: Trait> {267 268 TotalCollectionsLimitExceeded,269 270 CollectionDecimalPointLimitExceeded, 271 272 CollectionNameLimitExceeded, 273 274 CollectionDescriptionLimitExceeded, 275 276 CollectionTokenPrefixLimitExceeded,277 278 CollectionNotFound,279 280 TokenNotFound,281 282 NumOverflow, 283 284 AlreadyAdmin, 285 286 NoPermission,287 288 ConfirmUnsetSponsorFail,289 290 PublicMintingNotAllowed,291 292 MustBeTokenOwner,293 294 TokenValueTooLow,295 296 NftSizeLimitExceeded,297 298 FungibleUnexpectedParam,299 300 ApproveNotFound,301 302 TokenValueNotEnough,303 304 ApproveRequired,305 306 AddresNotInWhiteList,307 308 CollectionAdminsLimitExceeded,309 310 AddressOwnershipLimitExceeded,311 312 EmptyArgument,313 }314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319 320 type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326327328decl_storage! {329 trait Store for Module<T: Trait> as Nft {330331 332 NextCollectionID: u64;333 CreatedCollectionCount: u64;334 ChainVersion: u64;335 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337 338 pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340 341 CollectionCount: u64;342 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344 345 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349 350 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352 353 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355 356 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360 361 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363 364 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368 369 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;370 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;371 }372 add_extra_genesis {373 build(|config: &GenesisConfig<T>| {374 375 for (_num, _c) in &config.collection {376 <Module<T>>::init_collection(_c);377 }378379 for (_num, _q, _i) in &config.nft_item_id {380 <Module<T>>::init_nft_token(_i);381 }382383 for (_num, _q, _i) in &config.fungible_item_id {384 <Module<T>>::init_fungible_token(_i);385 }386387 for (_num, _q, _i) in &config.refungible_item_id {388 <Module<T>>::init_refungible_token(_i);389 }390 })391 }392}393394decl_event!(395 pub enum Event<T>396 where397 AccountId = <T as system::Trait>::AccountId,398 {399 400 401 402 403 404 405 406 407 408 Created(u64, u8, AccountId),409410 411 412 413 414 415 416 417 ItemCreated(u64, u64),418419 420 421 422 423 424 425 426 ItemDestroyed(u64, u64),427 }428);429430decl_module! {431 pub struct Module<T: Trait> for enum Call where origin: T::Origin {432433 fn deposit_event() = default;434 type Error = Error<T>;435436 fn on_initialize(now: T::BlockNumber) -> Weight {437438 if ChainVersion::get() < 2439 {440 let value = NextCollectionID::get();441 CreatedCollectionCount::put(value);442 ChainVersion::put(2);443 }444445 0446 }447448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 #[weight = T::WeightInfo::create_collection()]465 pub fn create_collection(origin,466 collection_name: Vec<u16>,467 collection_description: Vec<u16>,468 token_prefix: Vec<u8>,469 mode: CollectionMode) -> DispatchResult {470471 472 let who = ensure_signed(origin)?;473474 let decimal_points = match mode {475 CollectionMode::Fungible(points) => points,476 CollectionMode::ReFungible(points) => points,477 _ => 0478 };479480 481 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);482483 484 ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);485486 let mut name = collection_name.to_vec();487 name.push(0);488 ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);489490 let mut description = collection_description.to_vec();491 description.push(0);492 ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);493494 let mut prefix = token_prefix.to_vec();495 prefix.push(0);496 ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);497498 499 let next_id = CreatedCollectionCount::get()500 .checked_add(1)501 .ok_or(Error::<T>::NumOverflow)?;502503 504 let total = CollectionCount::get()505 .checked_add(1)506 .ok_or(Error::<T>::NumOverflow)?;507508 CreatedCollectionCount::put(next_id);509 CollectionCount::put(total);510511 512 let new_collection = CollectionType {513 owner: who.clone(),514 name: name,515 mode: mode.clone(),516 mint_mode: false,517 access: AccessMode::Normal,518 description: description,519 decimal_points: decimal_points,520 token_prefix: prefix,521 offchain_schema: Vec::new(),522 sponsor: T::AccountId::default(),523 unconfirmed_sponsor: T::AccountId::default(),524 variable_on_chain_schema: Vec::new(),525 const_on_chain_schema: Vec::new(),526 };527528 529 <Collection<T>>::insert(next_id, new_collection);530531 532 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));533534 Ok(())535 }536537 538 539 540 541 542 543 544 545 546 #[weight = T::WeightInfo::destroy_collection()]547 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {548549 let sender = ensure_signed(origin)?;550 Self::check_owner_permissions(collection_id, sender)?;551552 <AddressTokens<T>>::remove_prefix(collection_id);553 <ApprovedList<T>>::remove_prefix(collection_id);554 <Balance<T>>::remove_prefix(collection_id);555 <ItemListIndex>::remove(collection_id);556 <AdminList<T>>::remove(collection_id);557 <Collection<T>>::remove(collection_id);558 <WhiteList<T>>::remove(collection_id);559560 <NftItemList<T>>::remove_prefix(collection_id);561 <FungibleItemList<T>>::remove_prefix(collection_id);562 <ReFungibleItemList<T>>::remove_prefix(collection_id);563564 <NftTransferBasket<T>>::remove_prefix(collection_id);565 <FungibleTransferBasket<T>>::remove_prefix(collection_id);566 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);567568 if CollectionCount::get() > 0569 {570 571 let total = CollectionCount::get()572 .checked_sub(1)573 .ok_or(Error::<T>::NumOverflow)?;574575 CollectionCount::put(total);576 }577578 Ok(())579 }580581 582 583 584 585 586 587 588 589 590 591 592 593 #[weight = T::WeightInfo::add_to_white_list()]594 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{595596 let sender = ensure_signed(origin)?;597 Self::check_owner_or_admin_permissions(collection_id, sender)?;598599 let mut white_list_collection: Vec<T::AccountId>;600 if <WhiteList<T>>::contains_key(collection_id) {601 white_list_collection = <WhiteList<T>>::get(collection_id);602 if !white_list_collection.contains(&address.clone())603 {604 white_list_collection.push(address.clone());605 }606 }607 else {608 white_list_collection = Vec::new();609 white_list_collection.push(address.clone());610 }611612 <WhiteList<T>>::insert(collection_id, white_list_collection);613 Ok(())614 }615616 617 618 619 620 621 622 623 624 625 626 627 628 #[weight = T::WeightInfo::remove_from_white_list()]629 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{630631 let sender = ensure_signed(origin)?;632 Self::check_owner_or_admin_permissions(collection_id, sender)?;633634 if <WhiteList<T>>::contains_key(collection_id) {635 let mut white_list_collection = <WhiteList<T>>::get(collection_id);636 if white_list_collection.contains(&address.clone())637 {638 white_list_collection.retain(|i| *i != address.clone());639 <WhiteList<T>>::insert(collection_id, white_list_collection);640 }641 }642643 Ok(())644 }645646 647 648 649 650 651 652 653 654 655 656 657 #[weight = T::WeightInfo::set_public_access_mode()]658 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult659 {660 let sender = ensure_signed(origin)?;661662 Self::check_owner_permissions(collection_id, sender)?;663 let mut target_collection = <Collection<T>>::get(collection_id);664 target_collection.access = mode;665 <Collection<T>>::insert(collection_id, target_collection);666667 Ok(())668 }669670 671 672 673 674 675 676 677 678 679 680 681 682 683 #[weight = T::WeightInfo::set_mint_permission()]684 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult685 {686 let sender = ensure_signed(origin)?;687688 Self::check_owner_permissions(collection_id, sender)?;689 let mut target_collection = <Collection<T>>::get(collection_id);690 target_collection.mint_mode = mint_permission;691 <Collection<T>>::insert(collection_id, target_collection);692693 Ok(())694 }695696 697 698 699 700 701 702 703 704 705 706 707 #[weight = T::WeightInfo::change_collection_owner()]708 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {709710 let sender = ensure_signed(origin)?;711 Self::check_owner_permissions(collection_id, sender)?;712 let mut target_collection = <Collection<T>>::get(collection_id);713 target_collection.owner = new_owner;714 <Collection<T>>::insert(collection_id, target_collection);715716 Ok(())717 }718719 720 721 722 723 724 725 726 727 728 729 730 731 732 #[weight = T::WeightInfo::add_collection_admin()]733 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {734735 let sender = ensure_signed(origin)?;736 Self::check_owner_or_admin_permissions(collection_id, sender)?;737 let mut admin_arr: Vec<T::AccountId> = Vec::new();738739 if <AdminList<T>>::contains_key(collection_id)740 {741 admin_arr = <AdminList<T>>::get(collection_id);742 ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);743 }744745 746 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);747748 admin_arr.push(new_admin_id);749 <AdminList<T>>::insert(collection_id, admin_arr);750751 Ok(())752 }753754 755 756 757 758 759 760 761 762 763 764 765 766 #[weight = T::WeightInfo::remove_collection_admin()]767 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {768769 let sender = ensure_signed(origin)?;770 Self::check_owner_or_admin_permissions(collection_id, sender)?;771772 if <AdminList<T>>::contains_key(collection_id)773 {774 let mut admin_arr = <AdminList<T>>::get(collection_id);775 admin_arr.retain(|i| *i != account_id);776 <AdminList<T>>::insert(collection_id, admin_arr);777 }778779 Ok(())780 }781782 783 784 785 786 787 788 789 790 791 #[weight = T::WeightInfo::set_collection_sponsor()]792 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {793794 let sender = ensure_signed(origin)?;795 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);796797 let mut target_collection = <Collection<T>>::get(collection_id);798 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);799800 target_collection.unconfirmed_sponsor = new_sponsor;801 <Collection<T>>::insert(collection_id, target_collection);802803 Ok(())804 }805806 807 808 809 810 811 812 813 #[weight = T::WeightInfo::confirm_sponsorship()]814 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {815816 let sender = ensure_signed(origin)?;817 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);818819 let mut target_collection = <Collection<T>>::get(collection_id);820 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);821822 target_collection.sponsor = target_collection.unconfirmed_sponsor;823 target_collection.unconfirmed_sponsor = T::AccountId::default();824 <Collection<T>>::insert(collection_id, target_collection);825826 Ok(())827 }828829 830 831 832 833 834 835 836 837 838 #[weight = T::WeightInfo::remove_collection_sponsor()]839 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {840841 let sender = ensure_signed(origin)?;842 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);843844 let mut target_collection = <Collection<T>>::get(collection_id);845 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);846847 target_collection.sponsor = T::AccountId::default();848 <Collection<T>>::insert(collection_id, target_collection);849850 Ok(())851 }852853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876877 #[weight = T::WeightInfo::create_item(data.len())]878 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {879880 let sender = ensure_signed(origin)?;881882 Self::collection_exists(collection_id)?;883884 let target_collection = <Collection<T>>::get(collection_id);885886 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;887 Self::validate_create_item_args(&target_collection, &data)?;888 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;889890 Ok(())891 }892893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 #[weight = T::WeightInfo::create_item(items_data.into_iter()912 .map(|data| { data.len() })913 .sum())]914 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {915916 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);917 let sender = ensure_signed(origin)?;918919 Self::collection_exists(collection_id)?;920 let target_collection = <Collection<T>>::get(collection_id);921922 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;923924 for data in &items_data {925 Self::validate_create_item_args(&target_collection, data)?;926 }927 for data in &items_data {928 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;929 }930931 Ok(())932 }933934 935 936 937 938 939 940 941 942 943 944 945 946 947 #[weight = T::WeightInfo::burn_item()]948 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {949950 let sender = ensure_signed(origin)?;951 Self::collection_exists(collection_id)?;952953 954 let target_collection = <Collection<T>>::get(collection_id);955 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||956 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),957 Error::<T>::NoPermission);958959 if target_collection.access == AccessMode::WhiteList {960 Self::check_white_list(collection_id, &sender)?;961 }962963 match target_collection.mode964 {965 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,966 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,967 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,968 _ => ()969 };970971 972 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));973974 Ok(())975 }976977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 #[weight = T::WeightInfo::transfer()]1001 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10021003 let sender = ensure_signed(origin)?;10041005 1006 let target_collection = <Collection<T>>::get(collection_id);1007 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1008 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1009 Error::<T>::NoPermission);10101011 if target_collection.access == AccessMode::WhiteList {1012 Self::check_white_list(collection_id, &sender)?;1013 Self::check_white_list(collection_id, &recipient)?;1014 }10151016 match target_collection.mode1017 {1018 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1019 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1020 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1021 _ => ()1022 };10231024 Ok(())1025 }10261027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 #[weight = T::WeightInfo::approve()]1043 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10441045 let sender = ensure_signed(origin)?;10461047 1048 let target_collection = <Collection<T>>::get(collection_id);1049 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1050 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1051 Error::<T>::NoPermission);10521053 if target_collection.access == AccessMode::WhiteList {1054 Self::check_white_list(collection_id, &sender)?;1055 Self::check_white_list(collection_id, &approved)?;1056 }10571058 1059 let amount = 100000000;10601061 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1062 if list_exists {10631064 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1065 let item_contains = list.iter().any(|i| i.approved == approved);10661067 if !item_contains {1068 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1069 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1070 }1071 } else {10721073 let mut list = Vec::new();1074 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1075 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1076 }10771078 Ok(())1079 }1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 #[weight = T::WeightInfo::transfer_from()]1101 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11021103 let sender = ensure_signed(origin)?;1104 let mut appoved_transfer = false;11051106 1107 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1108 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1109 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1110 if opt_item.is_some()1111 {1112 appoved_transfer = true;1113 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1114 }1115 }11161117 1118 let target_collection = <Collection<T>>::get(collection_id);1119 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1120 Error::<T>::NoPermission);11211122 if target_collection.access == AccessMode::WhiteList {1123 Self::check_white_list(collection_id, &sender)?;1124 Self::check_white_list(collection_id, &recipient)?;1125 }11261127 1128 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1129 .into_iter().filter(|i| i.approved != sender.clone()).collect();1130 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113111321133 match target_collection.mode1134 {1135 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1136 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1137 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1138 _ => ()1139 };11401141 Ok(())1142 }11431144 1145 #[weight = 0]1146 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11471148 1149 1150 1151 11521153 11541155 11561157 Ok(())1158 }11591160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 #[weight = T::WeightInfo::set_variable_meta_data()]1173 pub fn set_variable_meta_data (1174 origin,1175 collection_id: u64,1176 item_id: u64,1177 data: Vec<u8>1178 ) -> DispatchResult {1179 let sender = ensure_signed(origin)?;1180 1181 Self::collection_exists(collection_id)?;11821183 1184 let target_collection = <Collection<T>>::get(collection_id);1185 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1186 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1187 Error::<T>::NoPermission);11881189 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11901191 match target_collection.mode1192 {1193 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1194 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1195 CollectionMode::Fungible(_) => fail!("Can't store metadata in fungible tokens."),1196 _ => fail!("Unexpected collection type.")1197 };11981199 Ok(())1200 }1201 12021203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 #[weight = T::WeightInfo::set_offchain_schema()]1216 pub fn set_offchain_schema(1217 origin,1218 collection_id: u64,1219 schema: Vec<u8>1220 ) -> DispatchResult {1221 let sender = ensure_signed(origin)?;1222 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12231224 let mut target_collection = <Collection<T>>::get(collection_id);1225 target_collection.offchain_schema = schema;1226 <Collection<T>>::insert(collection_id, target_collection);12271228 Ok(())1229 }12301231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 #[weight = T::WeightInfo::set_const_on_chain_schema()]1244 pub fn set_const_on_chain_schema (1245 origin,1246 collection_id: u64,1247 schema: Vec<u8>1248 ) -> DispatchResult {1249 let sender = ensure_signed(origin)?;1250 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12511252 let mut target_collection = <Collection<T>>::get(collection_id);1253 target_collection.const_on_chain_schema = schema;1254 <Collection<T>>::insert(collection_id, target_collection);12551256 Ok(())1257 }12581259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 #[weight = T::WeightInfo::set_const_on_chain_schema()]1272 pub fn set_variable_on_chain_schema (1273 origin,1274 collection_id: u64,1275 schema: Vec<u8>1276 ) -> DispatchResult {1277 let sender = ensure_signed(origin)?;1278 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12791280 let mut target_collection = <Collection<T>>::get(collection_id);1281 target_collection.variable_on_chain_schema = schema;1282 <Collection<T>>::insert(collection_id, target_collection);12831284 Ok(())1285 }12861287 1288 #[weight = 0]1289 pub fn set_chain_limits(1290 origin,1291 limits: ChainLimits1292 ) -> DispatchResult {1293 ensure_root(origin)?;1294 <ChainLimit>::put(limits);1295 Ok(())1296 }12971298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 #[weight = 0]1310 pub fn enable_contract_sponsoring(1311 origin,1312 contract_address: T::AccountId,1313 enable: bool1314 ) -> DispatchResult {1315 let sender = ensure_signed(origin)?;1316 let mut is_owner = false;1317 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1318 let owner = <ContractOwner<T>>::get(&contract_address);1319 is_owner = sender == owner;1320 }1321 ensure!(is_owner, Error::<T>::NoPermission);13221323 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1324 Ok(())1325 }13261327 }1328}13291330impl<T: Trait> Module<T> {13311332 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13331334 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1335 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1336 Self::check_white_list(collection_id, owner)?;1337 Self::check_white_list(collection_id, sender)?;1338 }13391340 Ok(())1341 }13421343 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1344 match target_collection.mode1345 {1346 CollectionMode::NFT => {1347 if let CreateItemData::NFT(data) = data {1348 1349 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1350 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1351 } else {1352 fail!("Not NFT item data used to mint in NFT collection.");1353 }1354 },1355 CollectionMode::Fungible(_) => {1356 if let CreateItemData::Fungible(_) = data {1357 } else {1358 fail!("Not Fungible item data used to mint in Fungible collection.");1359 }1360 },1361 CollectionMode::ReFungible(_) => {1362 if let CreateItemData::ReFungible(data) = data {13631364 1365 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1366 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1367 } else {1368 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1369 }1370 },1371 _ => { fail!("Unexpected collection type."); }1372 };13731374 Ok(())1375 }13761377 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1378 match data1379 {1380 CreateItemData::NFT(data) => {1381 let item = NftItemType {1382 collection: collection_id,1383 owner,1384 const_data: data.const_data,1385 variable_data: data.variable_data1386 };13871388 Self::add_nft_item(item)?;1389 },1390 CreateItemData::Fungible(_) => {1391 let item = FungibleItemType {1392 collection: collection_id,1393 owner,1394 value: (10 as u128).pow(collection.decimal_points)1395 };13961397 Self::add_fungible_item(item)?;1398 },1399 CreateItemData::ReFungible(data) => {1400 let mut owner_list = Vec::new();1401 let value = (10 as u128).pow(collection.decimal_points);1402 owner_list.push(Ownership {owner: owner.clone(), fraction: value});14031404 let item = ReFungibleItemType {1405 collection: collection_id,1406 owner: owner_list,1407 const_data: data.const_data,1408 variable_data: data.variable_data1409 };14101411 Self::add_refungible_item(item)?;1412 }1413 };141414151416 1417 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14181419 Ok(())1420 }14211422 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1423 let current_index = <ItemListIndex>::get(item.collection)1424 .checked_add(1)1425 .ok_or(Error::<T>::NumOverflow)?;1426 let itemcopy = item.clone();1427 let owner = item.owner.clone();1428 let value = item.value as u64;14291430 Self::add_token_index(item.collection, current_index, owner.clone())?;14311432 <ItemListIndex>::insert(item.collection, current_index);1433 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14341435 1436 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1437 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1438 1439 1440 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1441 .checked_add(value)1442 .ok_or(Error::<T>::NumOverflow)?;1443 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14441445 Ok(())1446 }14471448 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1449 let current_index = <ItemListIndex>::get(item.collection)1450 .checked_add(1)1451 .ok_or(Error::<T>::NumOverflow)?;1452 let itemcopy = item.clone();14531454 let value = item.owner.first().unwrap().fraction as u64;1455 let owner = item.owner.first().unwrap().owner.clone();14561457 Self::add_token_index(item.collection, current_index, owner.clone())?;14581459 <ItemListIndex>::insert(item.collection, current_index);1460 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14611462 1463 let block_number: T::BlockNumber = 0.into();1464 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14651466 1467 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1468 .checked_add(value)1469 .ok_or(Error::<T>::NumOverflow)?;1470 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14711472 Ok(())1473 }14741475 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1476 let current_index = <ItemListIndex>::get(item.collection)1477 .checked_add(1)1478 .ok_or(Error::<T>::NumOverflow)?;14791480 let item_owner = item.owner.clone();1481 let collection_id = item.collection.clone();1482 Self::add_token_index(collection_id, current_index, item.owner.clone())?;14831484 <ItemListIndex>::insert(collection_id, current_index);1485 <NftItemList<T>>::insert(collection_id, current_index, item);14861487 1488 let block_number: T::BlockNumber = 0.into();1489 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14901491 1492 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1493 .checked_add(1)1494 .ok_or(Error::<T>::NumOverflow)?;1495 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14961497 Ok(())1498 }14991500 fn burn_refungible_item(1501 collection_id: u64,1502 item_id: u64,1503 owner: T::AccountId,1504 ) -> DispatchResult {1505 ensure!(1506 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1507 Error::<T>::TokenNotFound1508 );1509 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1510 let item = collection1511 .owner1512 .iter()1513 .filter(|&i| i.owner == owner)1514 .next()1515 .unwrap();1516 Self::remove_token_index(collection_id, item_id, owner.clone())?;15171518 1519 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15201521 1522 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1523 .checked_sub(item.fraction as u64)1524 .ok_or(Error::<T>::NumOverflow)?;1525 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15261527 <ReFungibleItemList<T>>::remove(collection_id, item_id);15281529 Ok(())1530 }15311532 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1533 ensure!(1534 <NftItemList<T>>::contains_key(collection_id, item_id),1535 Error::<T>::TokenNotFound1536 );1537 let item = <NftItemList<T>>::get(collection_id, item_id);1538 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15391540 1541 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15421543 1544 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1545 .checked_sub(1)1546 .ok_or(Error::<T>::NumOverflow)?;1547 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1548 <NftItemList<T>>::remove(collection_id, item_id);15491550 Ok(())1551 }15521553 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1554 ensure!(1555 <FungibleItemList<T>>::contains_key(collection_id, item_id),1556 Error::<T>::TokenNotFound1557 );1558 let item = <FungibleItemList<T>>::get(collection_id, item_id);1559 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15601561 1562 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15631564 1565 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1566 .checked_sub(item.value as u64)1567 .ok_or(Error::<T>::NumOverflow)?;1568 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15691570 <FungibleItemList<T>>::remove(collection_id, item_id);15711572 Ok(())1573 }15741575 fn collection_exists(collection_id: u64) -> DispatchResult {1576 ensure!(1577 <Collection<T>>::contains_key(collection_id),1578 Error::<T>::CollectionNotFound1579 );1580 Ok(())1581 }15821583 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1584 Self::collection_exists(collection_id)?;15851586 let target_collection = <Collection<T>>::get(collection_id);1587 ensure!(1588 subject == target_collection.owner,1589 Error::<T>::NoPermission1590 );15911592 Ok(())1593 }15941595 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1596 let target_collection = <Collection<T>>::get(collection_id);1597 let mut result: bool = subject == target_collection.owner;1598 let exists = <AdminList<T>>::contains_key(collection_id);15991600 if !result & exists {1601 if <AdminList<T>>::get(collection_id).contains(&subject) {1602 result = true1603 }1604 }16051606 result1607 }16081609 fn check_owner_or_admin_permissions(1610 collection_id: u64,1611 subject: T::AccountId,1612 ) -> DispatchResult {1613 Self::collection_exists(collection_id)?;1614 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16151616 ensure!(1617 result,1618 Error::<T>::NoPermission1619 );1620 Ok(())1621 }16221623 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1624 let target_collection = <Collection<T>>::get(collection_id);16251626 match target_collection.mode {1627 CollectionMode::NFT => {1628 <NftItemList<T>>::get(collection_id, item_id).owner == subject1629 }1630 CollectionMode::Fungible(_) => {1631 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1632 }1633 CollectionMode::ReFungible(_) => {1634 <ReFungibleItemList<T>>::get(collection_id, item_id)1635 .owner1636 .iter()1637 .any(|i| i.owner == subject)1638 }1639 CollectionMode::Invalid => false,1640 }1641 }16421643 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1644 let mes = Error::<T>::AddresNotInWhiteList;1645 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1646 let wl = <WhiteList<T>>::get(collection_id);1647 ensure!(wl.contains(address), mes);16481649 Ok(())1650 }16511652 fn transfer_fungible(1653 collection_id: u64,1654 item_id: u64,1655 value: u64,1656 owner: T::AccountId,1657 new_owner: T::AccountId,1658 ) -> DispatchResult {1659 ensure!(1660 <FungibleItemList<T>>::contains_key(collection_id, item_id),1661 Error::<T>::TokenNotFound1662 );16631664 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1665 let amount = full_item.value;16661667 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);16681669 1670 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1671 .checked_sub(value)1672 .ok_or(Error::<T>::NumOverflow)?;1673 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16741675 let mut new_owner_account_id = 0;1676 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1677 if new_owner_items.len() > 0 {1678 new_owner_account_id = new_owner_items[0];1679 }16801681 let val64 = value.into();16821683 1684 if amount == val64 && new_owner_account_id == 0 {1685 1686 1687 let mut new_full_item = full_item.clone();1688 new_full_item.owner = new_owner.clone();1689 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16901691 1692 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1693 .checked_add(value)1694 .ok_or(Error::<T>::NumOverflow)?;1695 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16961697 1698 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1699 } else {1700 let mut new_full_item = full_item.clone();1701 new_full_item.value -= val64;17021703 1704 if new_owner_account_id > 0 {1705 1706 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1707 item.value += val64;17081709 1710 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1711 .checked_add(value)1712 .ok_or(Error::<T>::NumOverflow)?;1713 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17141715 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1716 } else {1717 1718 let item = FungibleItemType {1719 collection: collection_id,1720 owner: new_owner.clone(),1721 value: val64,1722 };17231724 Self::add_fungible_item(item)?;1725 }17261727 if amount == val64 {1728 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17291730 1731 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1732 <FungibleItemList<T>>::remove(collection_id, item_id);1733 }17341735 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1736 }17371738 Ok(())1739 }17401741 fn transfer_refungible(1742 collection_id: u64,1743 item_id: u64,1744 value: u64,1745 owner: T::AccountId,1746 new_owner: T::AccountId,1747 ) -> DispatchResult {1748 ensure!(1749 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1750 Error::<T>::TokenNotFound1751 );17521753 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1754 let item = full_item1755 .owner1756 .iter()1757 .filter(|i| i.owner == owner)1758 .next()1759 .ok_or(Error::<T>::NumOverflow)?;1760 let amount = item.fraction;17611762 ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17631764 1765 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1766 .checked_sub(value)1767 .ok_or(Error::<T>::NumOverflow)?;1768 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17691770 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1771 .checked_add(value)1772 .ok_or(Error::<T>::NumOverflow)?;1773 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17741775 let old_owner = item.owner.clone();1776 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1777 let val64 = value.into();17781779 1780 if amount == val64 && !new_owner_has_account {1781 1782 1783 let mut new_full_item = full_item.clone();1784 new_full_item1785 .owner1786 .iter_mut()1787 .find(|i| i.owner == owner)1788 .unwrap()1789 .owner = new_owner.clone();1790 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17911792 1793 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1794 } else {1795 let mut new_full_item = full_item.clone();1796 new_full_item1797 .owner1798 .iter_mut()1799 .find(|i| i.owner == owner)1800 .unwrap()1801 .fraction -= val64;18021803 1804 if new_owner_has_account {1805 1806 new_full_item1807 .owner1808 .iter_mut()1809 .find(|i| i.owner == new_owner)1810 .unwrap()1811 .fraction += val64;1812 } else {1813 1814 new_full_item.owner.push(Ownership {1815 owner: new_owner.clone(),1816 fraction: val64,1817 });1818 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1819 }18201821 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1822 }18231824 Ok(())1825 }18261827 fn transfer_nft(1828 collection_id: u64,1829 item_id: u64,1830 sender: T::AccountId,1831 new_owner: T::AccountId,1832 ) -> DispatchResult {1833 ensure!(1834 <NftItemList<T>>::contains_key(collection_id, item_id),1835 Error::<T>::TokenNotFound1836 );18371838 let mut item = <NftItemList<T>>::get(collection_id, item_id);18391840 ensure!(1841 sender == item.owner,1842 Error::<T>::MustBeTokenOwner1843 );18441845 1846 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1847 .checked_sub(1)1848 .ok_or(Error::<T>::NumOverflow)?;1849 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18501851 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1852 .checked_add(1)1853 .ok_or(Error::<T>::NumOverflow)?;1854 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18551856 1857 let old_owner = item.owner.clone();1858 item.owner = new_owner.clone();1859 <NftItemList<T>>::insert(collection_id, item_id, item);18601861 1862 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18631864 1865 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1866 Ok(())1867 }1868 1869 fn item_exists(1870 collection_id: u64,1871 item_id: u64,1872 mode: &CollectionMode1873 ) -> DispatchResult {1874 match mode {1875 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1876 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1877 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1878 _ => ()1879 };1880 1881 Ok(())1882 }18831884 fn set_re_fungible_variable_data(1885 collection_id: u64,1886 item_id: u64,1887 data: Vec<u8>1888 ) -> DispatchResult {1889 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18901891 item.variable_data = data;18921893 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18941895 Ok(())1896 }18971898 fn set_nft_variable_data(1899 collection_id: u64,1900 item_id: u64,1901 data: Vec<u8>1902 ) -> DispatchResult {1903 let mut item = <NftItemList<T>>::get(collection_id, item_id);1904 1905 item.variable_data = data;19061907 <NftItemList<T>>::insert(collection_id, item_id, item);1908 1909 Ok(())1910 }19111912 fn init_collection(item: &CollectionType<T::AccountId>) {1913 1914 assert!(1915 item.decimal_points <= 4,1916 "decimal_points parameter must be lower than 4"1917 );1918 assert!(1919 item.name.len() <= 64,1920 "Collection name can not be longer than 63 char"1921 );1922 assert!(1923 item.name.len() <= 256,1924 "Collection description can not be longer than 255 char"1925 );1926 assert!(1927 item.token_prefix.len() <= 16,1928 "Token prefix can not be longer than 15 char"1929 );19301931 1932 let next_id = CreatedCollectionCount::get()1933 .checked_add(1)1934 .unwrap();19351936 CreatedCollectionCount::put(next_id);1937 }19381939 fn init_nft_token(item: &NftItemType<T::AccountId>) {1940 let current_index = <ItemListIndex>::get(item.collection)1941 .checked_add(1)1942 .unwrap();19431944 let item_owner = item.owner.clone();1945 let collection_id = item.collection.clone();1946 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19471948 <ItemListIndex>::insert(collection_id, current_index);19491950 1951 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1952 .checked_add(1)1953 .unwrap();1954 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1955 }19561957 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1958 let current_index = <ItemListIndex>::get(item.collection)1959 .checked_add(1)1960 .unwrap();1961 let owner = item.owner.clone();1962 let value = item.value as u64;19631964 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19651966 <ItemListIndex>::insert(item.collection, current_index);19671968 1969 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1970 .checked_add(value)1971 .unwrap();1972 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1973 }19741975 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1976 let current_index = <ItemListIndex>::get(item.collection)1977 .checked_add(1)1978 .unwrap();19791980 let value = item.owner.first().unwrap().fraction as u64;1981 let owner = item.owner.first().unwrap().owner.clone();19821983 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19841985 <ItemListIndex>::insert(item.collection, current_index);19861987 1988 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1989 .checked_add(value)1990 .unwrap();1991 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1992 }19931994 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19951996 1997 if <AccountItemCount<T>>::contains_key(owner.clone()) {19981999 2000 let count = <AccountItemCount<T>>::get(owner.clone());2001 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20022003 <AccountItemCount<T>>::insert(owner.clone(), count2004 .checked_add(1)2005 .ok_or(Error::<T>::NumOverflow)?);2006 }2007 else {2008 <AccountItemCount<T>>::insert(owner.clone(), 1);2009 }20102011 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2012 if list_exists {2013 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2014 let item_contains = list.contains(&item_index.clone());20152016 if !item_contains {2017 list.push(item_index.clone());2018 }20192020 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2021 } else {2022 let mut itm = Vec::new();2023 itm.push(item_index.clone());2024 <AddressTokens<T>>::insert(collection_id, owner, itm);2025 2026 }20272028 Ok(())2029 }20302031 fn remove_token_index(2032 collection_id: u64,2033 item_index: u64,2034 owner: T::AccountId,2035 ) -> DispatchResult {20362037 2038 <AccountItemCount<T>>::insert(owner.clone(), 2039 <AccountItemCount<T>>::get(owner.clone())2040 .checked_sub(1)2041 .ok_or(Error::<T>::NumOverflow)?);204220432044 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2045 if list_exists {2046 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2047 let item_contains = list.contains(&item_index.clone());20482049 if item_contains {2050 list.retain(|&item| item != item_index);2051 <AddressTokens<T>>::insert(collection_id, owner, list);2052 }2053 }20542055 Ok(())2056 }20572058 fn move_token_index(2059 collection_id: u64,2060 item_index: u64,2061 old_owner: T::AccountId,2062 new_owner: T::AccountId,2063 ) -> DispatchResult {2064 Self::remove_token_index(collection_id, item_index, old_owner)?;2065 Self::add_token_index(collection_id, item_index, new_owner)?;20662067 Ok(())2068 }2069}2070207120722073207420752076pub type Multiplier = FixedU128;20772078type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2079 <T as system::Trait>::AccountId,2080>>::Balance;2081type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2082 <T as system::Trait>::AccountId,2083>>::NegativeImbalance;2084208520862087#[derive(Encode, Decode, Clone, Eq, PartialEq)]2088pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2089 #[codec(compact)] BalanceOf<T>2090);20912092impl<T: Trait + Send + Sync> sp_std::fmt::Debug2093 for ChargeTransactionPayment<T>2094{2095 #[cfg(feature = "std")]2096 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2097 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2098 }2099 #[cfg(not(feature = "std"))]2100 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2101 Ok(())2102 }2103}21042105impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2106where2107 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2108 BalanceOf<T>: Send + Sync + FixedPointOperand,2109{2110 2111 pub fn from(fee: BalanceOf<T>) -> Self {2112 Self(fee)2113 }21142115 pub fn traditional_fee(2116 len: usize,2117 info: &DispatchInfoOf<T::Call>,2118 tip: BalanceOf<T>,2119 ) -> BalanceOf<T>2120 where2121 T::Call: Dispatchable<Info = DispatchInfo>,2122 {2123 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2124 }21252126 fn withdraw_fee(2127 &self,2128 who: &T::AccountId,2129 call: &T::Call,2130 info: &DispatchInfoOf<T::Call>,2131 len: usize,2132 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2133 let tip = self.0;21342135 2136 2137 2138 2139 2140 2141 2142 let fee = Self::traditional_fee(len, info, tip);21432144 2145 2146 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2147 Some(Call::create_item(collection_id, _properties, _owner)) => {2148 <Collection<T>>::get(collection_id).sponsor2149 }2150 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2151 let _collection_mode = <Collection<T>>::get(collection_id).mode;21522153 2154 let sponsor_transfer = match _collection_mode {2155 CollectionMode::NFT => {2156 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2157 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2158 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2159 if block_number >= limit_time {2160 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2161 true2162 }2163 else {2164 false2165 }2166 }2167 CollectionMode::Fungible(_) => {2168 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2169 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2170 if basket.iter().any(|i| i.address == _new_owner.clone())2171 {2172 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2173 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2174 if block_number >= limit_time {2175 basket.retain(|x| x.address == item.address);2176 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2177 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2178 true2179 }2180 else {2181 false2182 }2183 }2184 else {2185 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2186 true2187 }2188 }2189 CollectionMode::ReFungible(_) => {2190 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2191 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2192 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2193 if block_number >= limit_time {2194 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2195 true2196 } else {2197 false2198 }2199 }2200 _ => {2201 false2202 },2203 };22042205 if !sponsor_transfer {2206 T::AccountId::default()2207 } else {2208 <Collection<T>>::get(collection_id).sponsor2209 }2210 }22112212 _ => T::AccountId::default(),2213 };22142215 2216 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22172218 2219 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22202221 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2222 code_hash,2223 &data,2224 &who,2225 );2226 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22272228 T::AccountId::default()2229 },22302231 2232 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22332234 let mut sp = T::AccountId::default();2235 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2236 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2237 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2238 sp = called_contract;2239 }2240 }22412242 sp2243 },22442245 _ => sponsor,2246 };22472248 let mut who_pays_fee: T::AccountId = sponsor.clone();2249 if sponsor == T::AccountId::default() {2250 who_pays_fee = who.clone();2251 }22522253 2254 if fee.is_zero() {2255 return Ok((fee, None));2256 }22572258 match <T as transaction_payment::Trait>::Currency::withdraw(2259 &who_pays_fee,2260 fee,2261 if tip.is_zero() {2262 WithdrawReason::TransactionPayment.into()2263 } else {2264 WithdrawReason::TransactionPayment | WithdrawReason::Tip2265 },2266 ExistenceRequirement::KeepAlive,2267 ) {2268 Ok(imbalance) => Ok((fee, Some(imbalance))),2269 Err(_) => Err(InvalidTransaction::Payment.into()),2270 }2271 }2272}227322742275impl<T: Trait + Send + Sync> SignedExtension2276 for ChargeTransactionPayment<T>2277where2278 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2279 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2280{2281 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2282 type AccountId = T::AccountId;2283 type Call = T::Call;2284 type AdditionalSigned = ();2285 type Pre = (2286 BalanceOf<T>,2287 Self::AccountId,2288 Option<NegativeImbalanceOf<T>>,2289 BalanceOf<T>,2290 );2291 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2292 Ok(())2293 }22942295 fn validate(2296 &self,2297 _who: &Self::AccountId,2298 _call: &Self::Call,2299 _info: &DispatchInfoOf<Self::Call>,2300 _len: usize,2301 ) -> TransactionValidity {2302 Ok(ValidTransaction::default())2303 }23042305 fn pre_dispatch(2306 self,2307 who: &Self::AccountId,2308 call: &Self::Call,2309 info: &DispatchInfoOf<Self::Call>,2310 len: usize,2311 ) -> Result<Self::Pre, TransactionValidityError> {2312 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2313 Ok((self.0, who.clone(), imbalance, fee))2314 }23152316 fn post_dispatch(2317 pre: Self::Pre,2318 info: &DispatchInfoOf<Self::Call>,2319 post_info: &PostDispatchInfoOf<Self::Call>,2320 len: usize,2321 _result: &DispatchResult,2322 ) -> Result<(), TransactionValidityError> {2323 let (tip, who, imbalance, fee) = pre;2324 if let Some(payed) = imbalance {2325 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2326 len as u32, info, post_info, tip,2327 );2328 let refund = fee.saturating_sub(actual_fee);2329 let actual_payment =2330 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2331 &who, refund,2332 ) {2333 Ok(refund_imbalance) => {2334 2335 2336 match payed.offset(refund_imbalance) {2337 Ok(actual_payment) => actual_payment,2338 Err(_) => return Err(InvalidTransaction::Payment.into()),2339 }2340 }2341 2342 2343 Err(_) => payed,2344 };2345 let imbalances = actual_payment.split(tip);2346 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2347 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2348 );2349 }2350 Ok(())2351 }2352}23532354