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,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}262263pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {264 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;265266 267 type WeightInfo: WeightInfo;268}269270#[cfg(feature = "runtime-benchmarks")]271mod benchmarking;272273274275decl_storage! {276 trait Store for Module<T: Trait> as Nft {277278 279 NextCollectionID: u64;280 CreatedCollectionCount: u64;281 ChainVersion: u64;282 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;283284 285 pub ChainLimit get(fn chain_limit) config(): ChainLimits;286287 288 CollectionCount: u64;289 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;290291 292 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;293 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;294 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;295296 297 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;298299 300 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;301302 303 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;304 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;305 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;306307 308 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;309310 311 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;312 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>>;313 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;314315 316 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;317 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;318 }319 add_extra_genesis {320 build(|config: &GenesisConfig<T>| {321 322 for (_num, _c) in &config.collection {323 <Module<T>>::init_collection(_c);324 }325326 for (_num, _q, _i) in &config.nft_item_id {327 <Module<T>>::init_nft_token(_i);328 }329330 for (_num, _q, _i) in &config.fungible_item_id {331 <Module<T>>::init_fungible_token(_i);332 }333334 for (_num, _q, _i) in &config.refungible_item_id {335 <Module<T>>::init_refungible_token(_i);336 }337 })338 }339}340341decl_event!(342 pub enum Event<T>343 where344 AccountId = <T as system::Trait>::AccountId,345 {346 347 348 349 350 351 352 353 354 355 Created(u64, u8, AccountId),356357 358 359 360 361 362 363 364 ItemCreated(u64, u64),365366 367 368 369 370 371 372 373 ItemDestroyed(u64, u64),374 }375);376377decl_module! {378 pub struct Module<T: Trait> for enum Call where origin: T::Origin {379380 fn deposit_event() = default;381382 fn on_initialize(now: T::BlockNumber) -> Weight {383384 if ChainVersion::get() < 2385 {386 let value = NextCollectionID::get();387 CreatedCollectionCount::put(value);388 ChainVersion::put(2);389 }390391 0392 }393394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 #[weight = T::WeightInfo::create_collection()]411 pub fn create_collection(origin,412 collection_name: Vec<u16>,413 collection_description: Vec<u16>,414 token_prefix: Vec<u8>,415 mode: CollectionMode) -> DispatchResult {416417 418 let who = ensure_signed(origin)?;419420 let decimal_points = match mode {421 CollectionMode::Fungible(points) => points,422 CollectionMode::ReFungible(points) => points,423 _ => 0424 };425426 427 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");428429 430 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");431432 let mut name = collection_name.to_vec();433 name.push(0);434 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");435436 let mut description = collection_description.to_vec();437 description.push(0);438 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");439440 let mut prefix = token_prefix.to_vec();441 prefix.push(0);442 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");443444 445 let next_id = CreatedCollectionCount::get()446 .checked_add(1)447 .expect("collection id error");448449 450 let total = CollectionCount::get()451 .checked_add(1)452 .expect("collection counter error");453454 CreatedCollectionCount::put(next_id);455 CollectionCount::put(total);456457 458 let new_collection = CollectionType {459 owner: who.clone(),460 name: name,461 mode: mode.clone(),462 mint_mode: false,463 access: AccessMode::Normal,464 description: description,465 decimal_points: decimal_points,466 token_prefix: prefix,467 offchain_schema: Vec::new(),468 sponsor: T::AccountId::default(),469 unconfirmed_sponsor: T::AccountId::default(),470 variable_on_chain_schema: Vec::new(),471 const_on_chain_schema: Vec::new(),472 };473474 475 <Collection<T>>::insert(next_id, new_collection);476477 478 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));479480 Ok(())481 }482483 484 485 486 487 488 489 490 491 492 #[weight = T::WeightInfo::destroy_collection()]493 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {494495 let sender = ensure_signed(origin)?;496 Self::check_owner_permissions(collection_id, sender)?;497498 <AddressTokens<T>>::remove_prefix(collection_id);499 <ApprovedList<T>>::remove_prefix(collection_id);500 <Balance<T>>::remove_prefix(collection_id);501 <ItemListIndex>::remove(collection_id);502 <AdminList<T>>::remove(collection_id);503 <Collection<T>>::remove(collection_id);504 <WhiteList<T>>::remove(collection_id);505506 <NftItemList<T>>::remove_prefix(collection_id);507 <FungibleItemList<T>>::remove_prefix(collection_id);508 <ReFungibleItemList<T>>::remove_prefix(collection_id);509510 <NftTransferBasket<T>>::remove_prefix(collection_id);511 <FungibleTransferBasket<T>>::remove_prefix(collection_id);512 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);513514 if CollectionCount::get() > 0515 {516 517 let total = CollectionCount::get()518 .checked_sub(1)519 .expect("collection counter error");520521 CollectionCount::put(total);522 }523524 Ok(())525 }526527 528 529 530 531 532 533 534 535 536 537 538 539 #[weight = T::WeightInfo::add_to_white_list()]540 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{541542 let sender = ensure_signed(origin)?;543 Self::check_owner_or_admin_permissions(collection_id, sender)?;544545 let mut white_list_collection: Vec<T::AccountId>;546 if <WhiteList<T>>::contains_key(collection_id) {547 white_list_collection = <WhiteList<T>>::get(collection_id);548 if !white_list_collection.contains(&address.clone())549 {550 white_list_collection.push(address.clone());551 }552 }553 else {554 white_list_collection = Vec::new();555 white_list_collection.push(address.clone());556 }557558 <WhiteList<T>>::insert(collection_id, white_list_collection);559 Ok(())560 }561562 563 564 565 566 567 568 569 570 571 572 573 574 #[weight = T::WeightInfo::remove_from_white_list()]575 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{576577 let sender = ensure_signed(origin)?;578 Self::check_owner_or_admin_permissions(collection_id, sender)?;579580 if <WhiteList<T>>::contains_key(collection_id) {581 let mut white_list_collection = <WhiteList<T>>::get(collection_id);582 if white_list_collection.contains(&address.clone())583 {584 white_list_collection.retain(|i| *i != address.clone());585 <WhiteList<T>>::insert(collection_id, white_list_collection);586 }587 }588589 Ok(())590 }591592 593 594 595 596 597 598 599 600 601 602 603 #[weight = T::WeightInfo::set_public_access_mode()]604 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult605 {606 let sender = ensure_signed(origin)?;607608 Self::check_owner_permissions(collection_id, sender)?;609 let mut target_collection = <Collection<T>>::get(collection_id);610 target_collection.access = mode;611 <Collection<T>>::insert(collection_id, target_collection);612613 Ok(())614 }615616 617 618 619 620 621 622 623 624 625 626 627 628 629 #[weight = T::WeightInfo::set_mint_permission()]630 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult631 {632 let sender = ensure_signed(origin)?;633634 Self::check_owner_permissions(collection_id, sender)?;635 let mut target_collection = <Collection<T>>::get(collection_id);636 target_collection.mint_mode = mint_permission;637 <Collection<T>>::insert(collection_id, target_collection);638639 Ok(())640 }641642 643 644 645 646 647 648 649 650 651 652 653 #[weight = T::WeightInfo::change_collection_owner()]654 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {655656 let sender = ensure_signed(origin)?;657 Self::check_owner_permissions(collection_id, sender)?;658 let mut target_collection = <Collection<T>>::get(collection_id);659 target_collection.owner = new_owner;660 <Collection<T>>::insert(collection_id, target_collection);661662 Ok(())663 }664665 666 667 668 669 670 671 672 673 674 675 676 677 678 #[weight = T::WeightInfo::add_collection_admin()]679 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {680681 let sender = ensure_signed(origin)?;682 Self::check_owner_or_admin_permissions(collection_id, sender)?;683 let mut admin_arr: Vec<T::AccountId> = Vec::new();684685 if <AdminList<T>>::contains_key(collection_id)686 {687 admin_arr = <AdminList<T>>::get(collection_id);688 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");689 }690691 692 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");693694 admin_arr.push(new_admin_id);695 <AdminList<T>>::insert(collection_id, admin_arr);696697 Ok(())698 }699700 701 702 703 704 705 706 707 708 709 710 711 712 #[weight = T::WeightInfo::remove_collection_admin()]713 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {714715 let sender = ensure_signed(origin)?;716 Self::check_owner_or_admin_permissions(collection_id, sender)?;717718 if <AdminList<T>>::contains_key(collection_id)719 {720 let mut admin_arr = <AdminList<T>>::get(collection_id);721 admin_arr.retain(|i| *i != account_id);722 <AdminList<T>>::insert(collection_id, admin_arr);723 }724725 Ok(())726 }727728 729 730 731 732 733 734 735 736 737 #[weight = T::WeightInfo::set_collection_sponsor()]738 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {739740 let sender = ensure_signed(origin)?;741 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");742743 let mut target_collection = <Collection<T>>::get(collection_id);744 ensure!(sender == target_collection.owner, "You do not own this collection");745746 target_collection.unconfirmed_sponsor = new_sponsor;747 <Collection<T>>::insert(collection_id, target_collection);748749 Ok(())750 }751752 753 754 755 756 757 758 759 #[weight = T::WeightInfo::confirm_sponsorship()]760 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {761762 let sender = ensure_signed(origin)?;763 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");764765 let mut target_collection = <Collection<T>>::get(collection_id);766 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");767768 target_collection.sponsor = target_collection.unconfirmed_sponsor;769 target_collection.unconfirmed_sponsor = T::AccountId::default();770 <Collection<T>>::insert(collection_id, target_collection);771772 Ok(())773 }774775 776 777 778 779 780 781 782 783 784 #[weight = T::WeightInfo::remove_collection_sponsor()]785 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {786787 let sender = ensure_signed(origin)?;788 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");789790 let mut target_collection = <Collection<T>>::get(collection_id);791 ensure!(sender == target_collection.owner, "You do not own this collection");792793 target_collection.sponsor = T::AccountId::default();794 <Collection<T>>::insert(collection_id, target_collection);795796 Ok(())797 }798799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822823 #[weight = T::WeightInfo::create_item(data.len())]824 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {825826 let sender = ensure_signed(origin)?;827828 Self::collection_exists(collection_id)?;829830 let target_collection = <Collection<T>>::get(collection_id);831832 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;833 Self::validate_create_item_args(&target_collection, &data)?;834 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;835836 Ok(())837 }838839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 #[weight = T::WeightInfo::create_item(items_data.into_iter()858 .map(|data| { data.len() })859 .sum())]860 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {861862 ensure!(items_data.len() > 0, "Length of items properties must be greater than 0.");863 let sender = ensure_signed(origin)?;864865 Self::collection_exists(collection_id)?;866 let target_collection = <Collection<T>>::get(collection_id);867868 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;869870 for data in &items_data {871 Self::validate_create_item_args(&target_collection, data)?;872 }873 for data in &items_data {874 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;875 }876877 Ok(())878 }879880 881 882 883 884 885 886 887 888 889 890 891 892 893 #[weight = T::WeightInfo::burn_item()]894 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {895896 let sender = ensure_signed(origin)?;897 Self::collection_exists(collection_id)?;898899 900 let target_collection = <Collection<T>>::get(collection_id);901 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||902 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),903 "Only item owner, collection owner and admins can modify item");904905 if target_collection.access == AccessMode::WhiteList {906 Self::check_white_list(collection_id, &sender)?;907 }908909 match target_collection.mode910 {911 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,912 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,913 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,914 _ => ()915 };916917 918 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));919920 Ok(())921 }922923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 #[weight = T::WeightInfo::transfer()]947 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {948949 let sender = ensure_signed(origin)?;950951 952 let target_collection = <Collection<T>>::get(collection_id);953 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||954 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),955 "Only item owner, collection owner and admins can modify item");956957 if target_collection.access == AccessMode::WhiteList {958 Self::check_white_list(collection_id, &sender)?;959 Self::check_white_list(collection_id, &recipient)?;960 }961962 match target_collection.mode963 {964 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,965 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,966 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,967 _ => ()968 };969970 Ok(())971 }972973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 #[weight = T::WeightInfo::approve()]989 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {990991 let sender = ensure_signed(origin)?;992993 994 let target_collection = <Collection<T>>::get(collection_id);995 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||996 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),997 "Only item owner, collection owner and admins can approve");998999 if target_collection.access == AccessMode::WhiteList {1000 Self::check_white_list(collection_id, &sender)?;1001 Self::check_white_list(collection_id, &approved)?;1002 }10031004 1005 let amount = 100000000;10061007 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1008 if list_exists {10091010 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1011 let item_contains = list.iter().any(|i| i.approved == approved);10121013 if !item_contains {1014 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1015 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1016 }1017 } else {10181019 let mut list = Vec::new();1020 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1021 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1022 }10231024 Ok(())1025 }1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 #[weight = T::WeightInfo::transfer_from()]1047 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10481049 let sender = ensure_signed(origin)?;1050 let mut appoved_transfer = false;10511052 1053 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1054 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1055 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1056 if opt_item.is_some()1057 {1058 appoved_transfer = true;1059 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1060 }1061 }10621063 1064 let target_collection = <Collection<T>>::get(collection_id);1065 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1066 "Only item owner, collection owner and admins can modify items");10671068 if target_collection.access == AccessMode::WhiteList {1069 Self::check_white_list(collection_id, &sender)?;1070 Self::check_white_list(collection_id, &recipient)?;1071 }10721073 1074 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1075 .into_iter().filter(|i| i.approved != sender.clone()).collect();1076 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);107710781079 match target_collection.mode1080 {1081 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1082 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1083 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1084 _ => ()1085 };10861087 Ok(())1088 }10891090 1091 #[weight = 0]1092 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10931094 1095 1096 1097 10981099 11001101 11021103 Ok(())1104 }11051106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 #[weight = T::WeightInfo::set_variable_meta_data()]1119 pub fn set_variable_meta_data (1120 origin,1121 collection_id: u64,1122 item_id: u64,1123 data: Vec<u8>1124 ) -> DispatchResult {1125 let sender = ensure_signed(origin)?;1126 1127 Self::collection_exists(collection_id)?;11281129 1130 let target_collection = <Collection<T>>::get(collection_id);1131 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1132 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1133 "Only item owner, collection owner and admins can modify item");11341135 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11361137 match target_collection.mode1138 {1139 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1140 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1141 _ => ()1142 };11431144 Ok(())1145 }1146 11471148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 #[weight = T::WeightInfo::set_offchain_schema()]1161 pub fn set_offchain_schema(1162 origin,1163 collection_id: u64,1164 schema: Vec<u8>1165 ) -> DispatchResult {1166 let sender = ensure_signed(origin)?;1167 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11681169 let mut target_collection = <Collection<T>>::get(collection_id);1170 target_collection.offchain_schema = schema;1171 <Collection<T>>::insert(collection_id, target_collection);11721173 Ok(())1174 }11751176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 #[weight = T::WeightInfo::set_const_on_chain_schema()]1189 pub fn set_const_on_chain_schema (1190 origin,1191 collection_id: u64,1192 schema: Vec<u8>1193 ) -> DispatchResult {1194 let sender = ensure_signed(origin)?;1195 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11961197 let mut target_collection = <Collection<T>>::get(collection_id);1198 target_collection.const_on_chain_schema = schema;1199 <Collection<T>>::insert(collection_id, target_collection);12001201 Ok(())1202 }12031204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 #[weight = T::WeightInfo::set_const_on_chain_schema()]1217 pub fn set_variable_on_chain_schema (1218 origin,1219 collection_id: u64,1220 schema: Vec<u8>1221 ) -> DispatchResult {1222 let sender = ensure_signed(origin)?;1223 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12241225 let mut target_collection = <Collection<T>>::get(collection_id);1226 target_collection.variable_on_chain_schema = schema;1227 <Collection<T>>::insert(collection_id, target_collection);12281229 Ok(())1230 }12311232 1233 #[weight = 0]1234 pub fn set_chain_limits(1235 origin,1236 limits: ChainLimits1237 ) -> DispatchResult {1238 ensure_root(origin)?;1239 <ChainLimit>::put(limits);1240 Ok(())1241 }12421243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 #[weight = 0]1255 pub fn enable_contract_sponsoring(1256 origin,1257 contract_address: T::AccountId,1258 enable: bool1259 ) -> DispatchResult {1260 let sender = ensure_signed(origin)?;1261 let mut is_owner = false;1262 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1263 let owner = <ContractOwner<T>>::get(&contract_address);1264 is_owner = sender == owner;1265 }1266 ensure!(is_owner, "Only contract owner may call this method");12671268 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1269 Ok(())1270 }12711272 }1273}12741275impl<T: Trait> Module<T> {12761277 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {12781279 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1280 ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1281 Self::check_white_list(collection_id, owner)?;1282 Self::check_white_list(collection_id, sender)?;1283 }12841285 Ok(())1286 }12871288 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1289 match target_collection.mode1290 {1291 CollectionMode::NFT => {1292 if let CreateItemData::NFT(data) = data {1293 1294 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1295 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1296 } else {1297 fail!("Not NFT item data used to mint in NFT collection.");1298 }1299 },1300 CollectionMode::Fungible(_) => {1301 if let CreateItemData::Fungible(_) = data {1302 } else {1303 fail!("Not Fungible item data used to mint in Fungible collection.");1304 }1305 },1306 CollectionMode::ReFungible(_) => {1307 if let CreateItemData::ReFungible(data) = data {13081309 1310 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1311 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1312 } else {1313 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1314 }1315 },1316 _ => { fail!("Unexpected collection type."); }1317 };13181319 Ok(())1320 }13211322 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1323 match data1324 {1325 CreateItemData::NFT(data) => {1326 let item = NftItemType {1327 collection: collection_id,1328 owner,1329 const_data: data.const_data,1330 variable_data: data.variable_data1331 };13321333 Self::add_nft_item(item)?;1334 },1335 CreateItemData::Fungible(_) => {1336 let item = FungibleItemType {1337 collection: collection_id,1338 owner,1339 value: (10 as u128).pow(collection.decimal_points)1340 };13411342 Self::add_fungible_item(item)?;1343 },1344 CreateItemData::ReFungible(data) => {1345 let mut owner_list = Vec::new();1346 let value = (10 as u128).pow(collection.decimal_points);1347 owner_list.push(Ownership {owner: owner.clone(), fraction: value});13481349 let item = ReFungibleItemType {1350 collection: collection_id,1351 owner: owner_list,1352 const_data: data.const_data,1353 variable_data: data.variable_data1354 };13551356 Self::add_refungible_item(item)?;1357 }1358 };135913601361 1362 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));13631364 Ok(())1365 }13661367 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1368 let current_index = <ItemListIndex>::get(item.collection)1369 .checked_add(1)1370 .expect("Item list index id error");1371 let itemcopy = item.clone();1372 let owner = item.owner.clone();1373 let value = item.value as u64;13741375 Self::add_token_index(item.collection, current_index, owner.clone())?;13761377 <ItemListIndex>::insert(item.collection, current_index);1378 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13791380 1381 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1382 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1383 1384 1385 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1386 .checked_add(value)1387 .unwrap();1388 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13891390 Ok(())1391 }13921393 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1394 let current_index = <ItemListIndex>::get(item.collection)1395 .checked_add(1)1396 .expect("Item list index id error");1397 let itemcopy = item.clone();13981399 let value = item.owner.first().unwrap().fraction as u64;1400 let owner = item.owner.first().unwrap().owner.clone();14011402 Self::add_token_index(item.collection, current_index, owner.clone())?;14031404 <ItemListIndex>::insert(item.collection, current_index);1405 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14061407 1408 let block_number: T::BlockNumber = 0.into();1409 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14101411 1412 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1413 .checked_add(value)1414 .unwrap();1415 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14161417 Ok(())1418 }14191420 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1421 let current_index = <ItemListIndex>::get(item.collection)1422 .checked_add(1)1423 .expect("Item list index id error");14241425 let item_owner = item.owner.clone();1426 let collection_id = item.collection.clone();1427 Self::add_token_index(collection_id, current_index, item.owner.clone())?;14281429 <ItemListIndex>::insert(collection_id, current_index);1430 <NftItemList<T>>::insert(collection_id, current_index, item);14311432 1433 let block_number: T::BlockNumber = 0.into();1434 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14351436 1437 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1438 .checked_add(1)1439 .unwrap();1440 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14411442 Ok(())1443 }14441445 fn burn_refungible_item(1446 collection_id: u64,1447 item_id: u64,1448 owner: T::AccountId,1449 ) -> DispatchResult {1450 ensure!(1451 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1452 "Item does not exists"1453 );1454 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1455 let item = collection1456 .owner1457 .iter()1458 .filter(|&i| i.owner == owner)1459 .next()1460 .unwrap();1461 Self::remove_token_index(collection_id, item_id, owner.clone())?;14621463 1464 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14651466 1467 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1468 .checked_sub(item.fraction as u64)1469 .unwrap();1470 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14711472 <ReFungibleItemList<T>>::remove(collection_id, item_id);14731474 Ok(())1475 }14761477 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1478 ensure!(1479 <NftItemList<T>>::contains_key(collection_id, item_id),1480 "Item does not exists"1481 );1482 let item = <NftItemList<T>>::get(collection_id, item_id);1483 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14841485 1486 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14871488 1489 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1490 .checked_sub(1)1491 .unwrap();1492 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1493 <NftItemList<T>>::remove(collection_id, item_id);14941495 Ok(())1496 }14971498 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1499 ensure!(1500 <FungibleItemList<T>>::contains_key(collection_id, item_id),1501 "Item does not exists"1502 );1503 let item = <FungibleItemList<T>>::get(collection_id, item_id);1504 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15051506 1507 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15081509 1510 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1511 .checked_sub(item.value as u64)1512 .unwrap();1513 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15141515 <FungibleItemList<T>>::remove(collection_id, item_id);15161517 Ok(())1518 }15191520 fn collection_exists(collection_id: u64) -> DispatchResult {1521 ensure!(1522 <Collection<T>>::contains_key(collection_id),1523 "This collection does not exist"1524 );1525 Ok(())1526 }15271528 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1529 Self::collection_exists(collection_id)?;15301531 let target_collection = <Collection<T>>::get(collection_id);1532 ensure!(1533 subject == target_collection.owner,1534 "You do not own this collection"1535 );15361537 Ok(())1538 }15391540 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1541 let target_collection = <Collection<T>>::get(collection_id);1542 let mut result: bool = subject == target_collection.owner;1543 let exists = <AdminList<T>>::contains_key(collection_id);15441545 if !result & exists {1546 if <AdminList<T>>::get(collection_id).contains(&subject) {1547 result = true1548 }1549 }15501551 result1552 }15531554 fn check_owner_or_admin_permissions(1555 collection_id: u64,1556 subject: T::AccountId,1557 ) -> DispatchResult {1558 Self::collection_exists(collection_id)?;1559 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15601561 ensure!(1562 result,1563 "You do not have permissions to modify this collection"1564 );1565 Ok(())1566 }15671568 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1569 let target_collection = <Collection<T>>::get(collection_id);15701571 match target_collection.mode {1572 CollectionMode::NFT => {1573 <NftItemList<T>>::get(collection_id, item_id).owner == subject1574 }1575 CollectionMode::Fungible(_) => {1576 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1577 }1578 CollectionMode::ReFungible(_) => {1579 <ReFungibleItemList<T>>::get(collection_id, item_id)1580 .owner1581 .iter()1582 .any(|i| i.owner == subject)1583 }1584 CollectionMode::Invalid => false,1585 }1586 }15871588 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1589 let mes = "Address is not in white list";1590 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1591 let wl = <WhiteList<T>>::get(collection_id);1592 ensure!(wl.contains(address), mes);15931594 Ok(())1595 }15961597 fn transfer_fungible(1598 collection_id: u64,1599 item_id: u64,1600 value: u64,1601 owner: T::AccountId,1602 new_owner: T::AccountId,1603 ) -> DispatchResult {1604 ensure!(1605 <FungibleItemList<T>>::contains_key(collection_id, item_id),1606 "Item not exists"1607 );16081609 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1610 let amount = full_item.value;16111612 ensure!(amount >= value.into(), "Item balance not enouth");16131614 1615 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1616 .checked_sub(value)1617 .unwrap();1618 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16191620 let mut new_owner_account_id = 0;1621 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1622 if new_owner_items.len() > 0 {1623 new_owner_account_id = new_owner_items[0];1624 }16251626 let val64 = value.into();16271628 1629 if amount == val64 && new_owner_account_id == 0 {1630 1631 1632 let mut new_full_item = full_item.clone();1633 new_full_item.owner = new_owner.clone();1634 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16351636 1637 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1638 .checked_add(value)1639 .unwrap();1640 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16411642 1643 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1644 } else {1645 let mut new_full_item = full_item.clone();1646 new_full_item.value -= val64;16471648 1649 if new_owner_account_id > 0 {1650 1651 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1652 item.value += val64;16531654 1655 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1656 .checked_add(value)1657 .unwrap();1658 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16591660 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1661 } else {1662 1663 let item = FungibleItemType {1664 collection: collection_id,1665 owner: new_owner.clone(),1666 value: val64,1667 };16681669 Self::add_fungible_item(item)?;1670 }16711672 if amount == val64 {1673 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16741675 1676 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1677 <FungibleItemList<T>>::remove(collection_id, item_id);1678 }16791680 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1681 }16821683 Ok(())1684 }16851686 fn transfer_refungible(1687 collection_id: u64,1688 item_id: u64,1689 value: u64,1690 owner: T::AccountId,1691 new_owner: T::AccountId,1692 ) -> DispatchResult {1693 ensure!(1694 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1695 "Item not exists"1696 );16971698 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1699 let item = full_item1700 .owner1701 .iter()1702 .filter(|i| i.owner == owner)1703 .next()1704 .unwrap();1705 let amount = item.fraction;17061707 ensure!(amount >= value.into(), "Item balance not enouth");17081709 1710 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1711 .checked_sub(value)1712 .unwrap();1713 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17141715 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1716 .checked_add(value)1717 .unwrap();1718 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17191720 let old_owner = item.owner.clone();1721 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1722 let val64 = value.into();17231724 1725 if amount == val64 && !new_owner_has_account {1726 1727 1728 let mut new_full_item = full_item.clone();1729 new_full_item1730 .owner1731 .iter_mut()1732 .find(|i| i.owner == owner)1733 .unwrap()1734 .owner = new_owner.clone();1735 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17361737 1738 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1739 } else {1740 let mut new_full_item = full_item.clone();1741 new_full_item1742 .owner1743 .iter_mut()1744 .find(|i| i.owner == owner)1745 .unwrap()1746 .fraction -= val64;17471748 1749 if new_owner_has_account {1750 1751 new_full_item1752 .owner1753 .iter_mut()1754 .find(|i| i.owner == new_owner)1755 .unwrap()1756 .fraction += val64;1757 } else {1758 1759 new_full_item.owner.push(Ownership {1760 owner: new_owner.clone(),1761 fraction: val64,1762 });1763 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1764 }17651766 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1767 }17681769 Ok(())1770 }17711772 fn transfer_nft(1773 collection_id: u64,1774 item_id: u64,1775 sender: T::AccountId,1776 new_owner: T::AccountId,1777 ) -> DispatchResult {1778 ensure!(1779 <NftItemList<T>>::contains_key(collection_id, item_id),1780 "Item not exists"1781 );17821783 let mut item = <NftItemList<T>>::get(collection_id, item_id);17841785 ensure!(1786 sender == item.owner,1787 "sender parameter and item owner must be equal"1788 );17891790 1791 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1792 .checked_sub(1)1793 .unwrap();1794 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17951796 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1797 .checked_add(1)1798 .unwrap();1799 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18001801 1802 let old_owner = item.owner.clone();1803 item.owner = new_owner.clone();1804 <NftItemList<T>>::insert(collection_id, item_id, item);18051806 1807 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18081809 1810 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1811 Ok(())1812 }1813 1814 fn item_exists(1815 collection_id: u64,1816 item_id: u64,1817 mode: &CollectionMode1818 ) -> DispatchResult {1819 match mode {1820 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1821 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1822 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1823 _ => ()1824 };1825 1826 Ok(())1827 }18281829 fn set_re_fungible_variable_data(1830 collection_id: u64,1831 item_id: u64,1832 data: Vec<u8>1833 ) -> DispatchResult {1834 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18351836 item.variable_data = data;18371838 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18391840 Ok(())1841 }18421843 fn set_nft_variable_data(1844 collection_id: u64,1845 item_id: u64,1846 data: Vec<u8>1847 ) -> DispatchResult {1848 let mut item = <NftItemList<T>>::get(collection_id, item_id);1849 1850 item.variable_data = data;18511852 <NftItemList<T>>::insert(collection_id, item_id, item);1853 1854 Ok(())1855 }18561857 fn init_collection(item: &CollectionType<T::AccountId>) {1858 1859 assert!(1860 item.decimal_points <= 4,1861 "decimal_points parameter must be lower than 4"1862 );1863 assert!(1864 item.name.len() <= 64,1865 "Collection name can not be longer than 63 char"1866 );1867 assert!(1868 item.name.len() <= 256,1869 "Collection description can not be longer than 255 char"1870 );1871 assert!(1872 item.token_prefix.len() <= 16,1873 "Token prefix can not be longer than 15 char"1874 );18751876 1877 let next_id = CreatedCollectionCount::get()1878 .checked_add(1)1879 .expect("collection id error");18801881 CreatedCollectionCount::put(next_id);1882 }18831884 fn init_nft_token(item: &NftItemType<T::AccountId>) {1885 let current_index = <ItemListIndex>::get(item.collection)1886 .checked_add(1)1887 .expect("Item list index id error");18881889 let item_owner = item.owner.clone();1890 let collection_id = item.collection.clone();1891 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18921893 <ItemListIndex>::insert(collection_id, current_index);18941895 1896 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1897 .checked_add(1)1898 .unwrap();1899 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1900 }19011902 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1903 let current_index = <ItemListIndex>::get(item.collection)1904 .checked_add(1)1905 .expect("Item list index id error");1906 let owner = item.owner.clone();1907 let value = item.value as u64;19081909 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19101911 <ItemListIndex>::insert(item.collection, current_index);19121913 1914 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1915 .checked_add(value)1916 .unwrap();1917 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1918 }19191920 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1921 let current_index = <ItemListIndex>::get(item.collection)1922 .checked_add(1)1923 .expect("Item list index id error");19241925 let value = item.owner.first().unwrap().fraction as u64;1926 let owner = item.owner.first().unwrap().owner.clone();19271928 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19291930 <ItemListIndex>::insert(item.collection, current_index);19311932 1933 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1934 .checked_add(value)1935 .unwrap();1936 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1937 }19381939 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19401941 1942 if <AccountItemCount<T>>::contains_key(owner.clone()) {19431944 1945 let count = <AccountItemCount<T>>::get(owner.clone());1946 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");19471948 <AccountItemCount<T>>::insert(owner.clone(), 1949 count.checked_add(1).unwrap());1950 }1951 else {1952 <AccountItemCount<T>>::insert(owner.clone(), 1);1953 }19541955 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1956 if list_exists {1957 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1958 let item_contains = list.contains(&item_index.clone());19591960 if !item_contains {1961 list.push(item_index.clone());1962 }19631964 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1965 } else {1966 let mut itm = Vec::new();1967 itm.push(item_index.clone());1968 <AddressTokens<T>>::insert(collection_id, owner, itm);1969 1970 }19711972 Ok(())1973 }19741975 fn remove_token_index(1976 collection_id: u64,1977 item_index: u64,1978 owner: T::AccountId,1979 ) -> DispatchResult {19801981 1982 <AccountItemCount<T>>::insert(owner.clone(), 1983 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());198419851986 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1987 if list_exists {1988 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1989 let item_contains = list.contains(&item_index.clone());19901991 if item_contains {1992 list.retain(|&item| item != item_index);1993 <AddressTokens<T>>::insert(collection_id, owner, list);1994 }1995 }19961997 Ok(())1998 }19992000 fn move_token_index(2001 collection_id: u64,2002 item_index: u64,2003 old_owner: T::AccountId,2004 new_owner: T::AccountId,2005 ) -> DispatchResult {2006 Self::remove_token_index(collection_id, item_index, old_owner)?;2007 Self::add_token_index(collection_id, item_index, new_owner)?;20082009 Ok(())2010 }2011}2012201320142015201620172018pub type Multiplier = FixedU128;20192020type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2021 <T as system::Trait>::AccountId,2022>>::Balance;2023type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2024 <T as system::Trait>::AccountId,2025>>::NegativeImbalance;2026202720282029#[derive(Encode, Decode, Clone, Eq, PartialEq)]2030pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2031 #[codec(compact)] BalanceOf<T>2032);20332034impl<T: Trait + Send + Sync> sp_std::fmt::Debug2035 for ChargeTransactionPayment<T>2036{2037 #[cfg(feature = "std")]2038 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2039 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2040 }2041 #[cfg(not(feature = "std"))]2042 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2043 Ok(())2044 }2045}20462047impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2048where2049 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2050 BalanceOf<T>: Send + Sync + FixedPointOperand,2051{2052 2053 pub fn from(fee: BalanceOf<T>) -> Self {2054 Self(fee)2055 }20562057 pub fn traditional_fee(2058 len: usize,2059 info: &DispatchInfoOf<T::Call>,2060 tip: BalanceOf<T>,2061 ) -> BalanceOf<T>2062 where2063 T::Call: Dispatchable<Info = DispatchInfo>,2064 {2065 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2066 }20672068 fn withdraw_fee(2069 &self,2070 who: &T::AccountId,2071 call: &T::Call,2072 info: &DispatchInfoOf<T::Call>,2073 len: usize,2074 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2075 let tip = self.0;20762077 2078 2079 2080 2081 2082 2083 2084 let fee = Self::traditional_fee(len, info, tip);20852086 2087 2088 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2089 Some(Call::create_item(collection_id, _properties, _owner)) => {2090 <Collection<T>>::get(collection_id).sponsor2091 }2092 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2093 let _collection_mode = <Collection<T>>::get(collection_id).mode;20942095 2096 let sponsor_transfer = match _collection_mode {2097 CollectionMode::NFT => {2098 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2099 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2100 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2101 if block_number >= limit_time {2102 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2103 true2104 }2105 else {2106 false2107 }2108 }2109 CollectionMode::Fungible(_) => {2110 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2111 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2112 if basket.iter().any(|i| i.address == _new_owner.clone())2113 {2114 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2115 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2116 if block_number >= limit_time {2117 basket.retain(|x| x.address == item.address);2118 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2119 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2120 true2121 }2122 else {2123 false2124 }2125 }2126 else {2127 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2128 true2129 }2130 }2131 CollectionMode::ReFungible(_) => {2132 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2133 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2134 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2135 if block_number >= limit_time {2136 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2137 true2138 } else {2139 false2140 }2141 }2142 _ => {2143 false2144 },2145 };21462147 if !sponsor_transfer {2148 T::AccountId::default()2149 } else {2150 <Collection<T>>::get(collection_id).sponsor2151 }2152 }21532154 _ => T::AccountId::default(),2155 };21562157 2158 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21592160 2161 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21622163 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2164 code_hash,2165 &data,2166 &who,2167 );2168 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21692170 T::AccountId::default()2171 },21722173 2174 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21752176 let mut sp = T::AccountId::default();2177 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2178 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2179 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2180 sp = called_contract;2181 }2182 }21832184 sp2185 },21862187 _ => sponsor,2188 };21892190 let mut who_pays_fee: T::AccountId = sponsor.clone();2191 if sponsor == T::AccountId::default() {2192 who_pays_fee = who.clone();2193 }21942195 2196 if fee.is_zero() {2197 return Ok((fee, None));2198 }21992200 match <T as transaction_payment::Trait>::Currency::withdraw(2201 &who_pays_fee,2202 fee,2203 if tip.is_zero() {2204 WithdrawReason::TransactionPayment.into()2205 } else {2206 WithdrawReason::TransactionPayment | WithdrawReason::Tip2207 },2208 ExistenceRequirement::KeepAlive,2209 ) {2210 Ok(imbalance) => Ok((fee, Some(imbalance))),2211 Err(_) => Err(InvalidTransaction::Payment.into()),2212 }2213 }2214}221522162217impl<T: Trait + Send + Sync> SignedExtension2218 for ChargeTransactionPayment<T>2219where2220 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2221 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2222{2223 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2224 type AccountId = T::AccountId;2225 type Call = T::Call;2226 type AdditionalSigned = ();2227 type Pre = (2228 BalanceOf<T>,2229 Self::AccountId,2230 Option<NegativeImbalanceOf<T>>,2231 BalanceOf<T>,2232 );2233 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2234 Ok(())2235 }22362237 fn validate(2238 &self,2239 _who: &Self::AccountId,2240 _call: &Self::Call,2241 _info: &DispatchInfoOf<Self::Call>,2242 _len: usize,2243 ) -> TransactionValidity {2244 Ok(ValidTransaction::default())2245 }22462247 fn pre_dispatch(2248 self,2249 who: &Self::AccountId,2250 call: &Self::Call,2251 info: &DispatchInfoOf<Self::Call>,2252 len: usize,2253 ) -> Result<Self::Pre, TransactionValidityError> {2254 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2255 Ok((self.0, who.clone(), imbalance, fee))2256 }22572258 fn post_dispatch(2259 pre: Self::Pre,2260 info: &DispatchInfoOf<Self::Call>,2261 post_info: &PostDispatchInfoOf<Self::Call>,2262 len: usize,2263 _result: &DispatchResult,2264 ) -> Result<(), TransactionValidityError> {2265 let (tip, who, imbalance, fee) = pre;2266 if let Some(payed) = imbalance {2267 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2268 len as u32, info, post_info, tip,2269 );2270 let refund = fee.saturating_sub(actual_fee);2271 let actual_payment =2272 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2273 &who, refund,2274 ) {2275 Ok(refund_imbalance) => {2276 2277 2278 match payed.offset(refund_imbalance) {2279 Ok(actual_payment) => actual_payment,2280 Err(_) => return Err(InvalidTransaction::Payment.into()),2281 }2282 }2283 2284 2285 Err(_) => payed,2286 };2287 let imbalances = actual_payment.split(tip);2288 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2289 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2290 );2291 }2292 Ok(())2293 }2294}22952296