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 CollectionAdminsType<AccountId> {119 pub admin: AccountId,120 pub collection_id: u64,121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct NftItemType<AccountId> {126 pub collection: u64,127 pub owner: AccountId,128 pub const_data: Vec<u8>,129 pub variable_data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135 pub collection: u64,136 pub owner: AccountId,137 pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143 pub collection: u64,144 pub owner: Vec<Ownership<AccountId>>,145 pub const_data: Vec<u8>,146 pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct ApprovePermissions<AccountId> {152 pub approved: AccountId,153 pub amount: u64,154}155156#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]157#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]158pub struct VestingItem<AccountId, Moment> {159 pub sender: AccountId,160 pub recipient: AccountId,161 pub collection_id: u64,162 pub item_id: u64,163 pub amount: u64,164 pub vesting_date: Moment,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct BasketItem<AccountId, BlockNumber> {170 pub address: AccountId,171 pub start_block: BlockNumber,172}173174#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]175#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]176pub struct ChainLimits {177 pub collection_numbers_limit: u64,178 pub account_token_ownership_limit: u64,179 pub collections_admins_limit: u64,180 pub custom_data_limit: u32,181182 183 pub nft_sponsor_transfer_timeout: u32,184 pub fungible_sponsor_transfer_timeout: u32,185 pub refungible_sponsor_transfer_timeout: u32,186}187188pub trait WeightInfo {189 fn create_collection() -> Weight;190 fn destroy_collection() -> Weight;191 fn add_to_white_list() -> Weight;192 fn remove_from_white_list() -> Weight;193 fn set_public_access_mode() -> Weight;194 fn set_mint_permission() -> Weight;195 fn change_collection_owner() -> Weight;196 fn add_collection_admin() -> Weight;197 fn remove_collection_admin() -> Weight;198 fn set_collection_sponsor() -> Weight;199 fn confirm_sponsorship() -> Weight;200 fn remove_collection_sponsor() -> Weight;201 fn create_item(s: usize) -> Weight;202 fn burn_item() -> Weight;203 fn transfer() -> Weight;204 fn approve() -> Weight;205 fn transfer_from() -> Weight;206 fn set_offchain_schema() -> Weight;207 fn set_const_on_chain_schema() -> Weight;208 fn set_variable_on_chain_schema() -> Weight;209 fn set_variable_meta_data() -> Weight;210 211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216 pub const_data: Vec<u8>,217 pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228 pub const_data: Vec<u8>,229 pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235 NFT(CreateNftData),236 Fungible(CreateFungibleData),237 ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241 pub fn len(&self) -> usize {242 let len = match self {243 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245 _ => 0246 };247 248 return len;249 }250}251252impl From<CreateNftData> for CreateItemData {253 fn from(item: CreateNftData) -> Self {254 CreateItemData::NFT(item)255 }256}257258impl From<CreateReFungibleData> for CreateItemData {259 fn from(item: CreateReFungibleData) -> Self {260 CreateItemData::ReFungible(item)261 }262}263264impl From<CreateFungibleData> for CreateItemData {265 fn from(item: CreateFungibleData) -> Self {266 CreateItemData::Fungible(item)267 }268}269270pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {271 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;272273 274 type WeightInfo: WeightInfo;275}276277#[cfg(feature = "runtime-benchmarks")]278mod benchmarking;279280281282decl_storage! {283 trait Store for Module<T: Trait> as Nft {284285 286 NextCollectionID: u64;287 CreatedCollectionCount: u64;288 ChainVersion: u64;289 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;290291 292 pub ChainLimit get(fn chain_limit) config(): ChainLimits;293294 295 CollectionCount: u64;296 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;297298 299 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;300 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;301 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;302303 304 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;305306 307 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;308309 310 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;311 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;312 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;313314 315 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;316317 318 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;319 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>>;320 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;321322 323 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;324 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;325 }326 add_extra_genesis {327 build(|config: &GenesisConfig<T>| {328 329 for (_num, _c) in &config.collection {330 <Module<T>>::init_collection(_c);331 }332333 for (_num, _q, _i) in &config.nft_item_id {334 <Module<T>>::init_nft_token(_i);335 }336337 for (_num, _q, _i) in &config.fungible_item_id {338 <Module<T>>::init_fungible_token(_i);339 }340341 for (_num, _q, _i) in &config.refungible_item_id {342 <Module<T>>::init_refungible_token(_i);343 }344 })345 }346}347348decl_event!(349 pub enum Event<T>350 where351 AccountId = <T as system::Trait>::AccountId,352 {353 354 355 356 357 358 359 360 361 362 Created(u64, u8, AccountId),363364 365 366 367 368 369 370 371 ItemCreated(u64, u64),372373 374 375 376 377 378 379 380 ItemDestroyed(u64, u64),381 }382);383384decl_module! {385 pub struct Module<T: Trait> for enum Call where origin: T::Origin {386387 fn deposit_event() = default;388389 fn on_initialize(now: T::BlockNumber) -> Weight {390391 if ChainVersion::get() < 2392 {393 let value = NextCollectionID::get();394 CreatedCollectionCount::put(value);395 ChainVersion::put(2);396 }397398 0399 }400401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 #[weight = T::WeightInfo::create_collection()]418 pub fn create_collection(origin,419 collection_name: Vec<u16>,420 collection_description: Vec<u16>,421 token_prefix: Vec<u8>,422 mode: CollectionMode) -> DispatchResult {423424 425 let who = ensure_signed(origin)?;426427 let decimal_points = match mode {428 CollectionMode::Fungible(points) => points,429 CollectionMode::ReFungible(points) => points,430 _ => 0431 };432433 434 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");435436 437 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");438439 let mut name = collection_name.to_vec();440 name.push(0);441 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");442443 let mut description = collection_description.to_vec();444 description.push(0);445 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");446447 let mut prefix = token_prefix.to_vec();448 prefix.push(0);449 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");450451 452 let next_id = CreatedCollectionCount::get()453 .checked_add(1)454 .expect("collection id error");455456 457 let total = CollectionCount::get()458 .checked_add(1)459 .expect("collection counter error");460461 CreatedCollectionCount::put(next_id);462 CollectionCount::put(total);463464 465 let new_collection = CollectionType {466 owner: who.clone(),467 name: name,468 mode: mode.clone(),469 mint_mode: false,470 access: AccessMode::Normal,471 description: description,472 decimal_points: decimal_points,473 token_prefix: prefix,474 offchain_schema: Vec::new(),475 sponsor: T::AccountId::default(),476 unconfirmed_sponsor: T::AccountId::default(),477 variable_on_chain_schema: Vec::new(),478 const_on_chain_schema: Vec::new(),479 };480481 482 <Collection<T>>::insert(next_id, new_collection);483484 485 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));486487 Ok(())488 }489490 491 492 493 494 495 496 497 498 499 #[weight = T::WeightInfo::destroy_collection()]500 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {501502 let sender = ensure_signed(origin)?;503 Self::check_owner_permissions(collection_id, sender)?;504505 <AddressTokens<T>>::remove_prefix(collection_id);506 <ApprovedList<T>>::remove_prefix(collection_id);507 <Balance<T>>::remove_prefix(collection_id);508 <ItemListIndex>::remove(collection_id);509 <AdminList<T>>::remove(collection_id);510 <Collection<T>>::remove(collection_id);511 <WhiteList<T>>::remove(collection_id);512513 <NftItemList<T>>::remove_prefix(collection_id);514 <FungibleItemList<T>>::remove_prefix(collection_id);515 <ReFungibleItemList<T>>::remove_prefix(collection_id);516517 <NftTransferBasket<T>>::remove_prefix(collection_id);518 <FungibleTransferBasket<T>>::remove_prefix(collection_id);519 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);520521 if CollectionCount::get() > 0522 {523 524 let total = CollectionCount::get()525 .checked_sub(1)526 .expect("collection counter error");527528 CollectionCount::put(total);529 }530531 Ok(())532 }533534 535 536 537 538 539 540 541 542 543 544 545 546 #[weight = T::WeightInfo::add_to_white_list()]547 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{548549 let sender = ensure_signed(origin)?;550 Self::check_owner_or_admin_permissions(collection_id, sender)?;551552 let mut white_list_collection: Vec<T::AccountId>;553 if <WhiteList<T>>::contains_key(collection_id) {554 white_list_collection = <WhiteList<T>>::get(collection_id);555 if !white_list_collection.contains(&address.clone())556 {557 white_list_collection.push(address.clone());558 }559 }560 else {561 white_list_collection = Vec::new();562 white_list_collection.push(address.clone());563 }564565 <WhiteList<T>>::insert(collection_id, white_list_collection);566 Ok(())567 }568569 570 571 572 573 574 575 576 577 578 579 580 581 #[weight = T::WeightInfo::remove_from_white_list()]582 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{583584 let sender = ensure_signed(origin)?;585 Self::check_owner_or_admin_permissions(collection_id, sender)?;586587 if <WhiteList<T>>::contains_key(collection_id) {588 let mut white_list_collection = <WhiteList<T>>::get(collection_id);589 if white_list_collection.contains(&address.clone())590 {591 white_list_collection.retain(|i| *i != address.clone());592 <WhiteList<T>>::insert(collection_id, white_list_collection);593 }594 }595596 Ok(())597 }598599 600 601 602 603 604 605 606 607 608 609 610 #[weight = T::WeightInfo::set_public_access_mode()]611 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult612 {613 let sender = ensure_signed(origin)?;614615 Self::check_owner_permissions(collection_id, sender)?;616 let mut target_collection = <Collection<T>>::get(collection_id);617 target_collection.access = mode;618 <Collection<T>>::insert(collection_id, target_collection);619620 Ok(())621 }622623 624 625 626 627 628 629 630 631 632 633 634 635 636 #[weight = T::WeightInfo::set_mint_permission()]637 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult638 {639 let sender = ensure_signed(origin)?;640641 Self::check_owner_permissions(collection_id, sender)?;642 let mut target_collection = <Collection<T>>::get(collection_id);643 target_collection.mint_mode = mint_permission;644 <Collection<T>>::insert(collection_id, target_collection);645646 Ok(())647 }648649 650 651 652 653 654 655 656 657 658 659 660 #[weight = T::WeightInfo::change_collection_owner()]661 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {662663 let sender = ensure_signed(origin)?;664 Self::check_owner_permissions(collection_id, sender)?;665 let mut target_collection = <Collection<T>>::get(collection_id);666 target_collection.owner = new_owner;667 <Collection<T>>::insert(collection_id, target_collection);668669 Ok(())670 }671672 673 674 675 676 677 678 679 680 681 682 683 684 685 #[weight = T::WeightInfo::add_collection_admin()]686 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {687688 let sender = ensure_signed(origin)?;689 Self::check_owner_or_admin_permissions(collection_id, sender)?;690 let mut admin_arr: Vec<T::AccountId> = Vec::new();691692 if <AdminList<T>>::contains_key(collection_id)693 {694 admin_arr = <AdminList<T>>::get(collection_id);695 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");696 }697698 699 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");700701 admin_arr.push(new_admin_id);702 <AdminList<T>>::insert(collection_id, admin_arr);703704 Ok(())705 }706707 708 709 710 711 712 713 714 715 716 717 718 719 #[weight = T::WeightInfo::remove_collection_admin()]720 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {721722 let sender = ensure_signed(origin)?;723 Self::check_owner_or_admin_permissions(collection_id, sender)?;724725 if <AdminList<T>>::contains_key(collection_id)726 {727 let mut admin_arr = <AdminList<T>>::get(collection_id);728 admin_arr.retain(|i| *i != account_id);729 <AdminList<T>>::insert(collection_id, admin_arr);730 }731732 Ok(())733 }734735 736 737 738 739 740 741 742 743 744 #[weight = T::WeightInfo::set_collection_sponsor()]745 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {746747 let sender = ensure_signed(origin)?;748 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");749750 let mut target_collection = <Collection<T>>::get(collection_id);751 ensure!(sender == target_collection.owner, "You do not own this collection");752753 target_collection.unconfirmed_sponsor = new_sponsor;754 <Collection<T>>::insert(collection_id, target_collection);755756 Ok(())757 }758759 760 761 762 763 764 765 766 #[weight = T::WeightInfo::confirm_sponsorship()]767 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {768769 let sender = ensure_signed(origin)?;770 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");771772 let mut target_collection = <Collection<T>>::get(collection_id);773 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");774775 target_collection.sponsor = target_collection.unconfirmed_sponsor;776 target_collection.unconfirmed_sponsor = T::AccountId::default();777 <Collection<T>>::insert(collection_id, target_collection);778779 Ok(())780 }781782 783 784 785 786 787 788 789 790 791 #[weight = T::WeightInfo::remove_collection_sponsor()]792 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {793794 let sender = ensure_signed(origin)?;795 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");796797 let mut target_collection = <Collection<T>>::get(collection_id);798 ensure!(sender == target_collection.owner, "You do not own this collection");799800 target_collection.sponsor = T::AccountId::default();801 <Collection<T>>::insert(collection_id, target_collection);802803 Ok(())804 }805806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829830 #[weight = T::WeightInfo::create_item(data.len())]831 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {832833 let sender = ensure_signed(origin)?;834835 Self::collection_exists(collection_id)?;836837 let target_collection = <Collection<T>>::get(collection_id);838839 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;840 Self::validate_create_item_args(&target_collection, &properties)?;841 Self::create_item_no_validation(collection_id, &target_collection, &properties, &owner)?;842843 Ok(())844 }845846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 #[weight = 0]865 pub fn create_multiple_items(origin, collection_id: u64, properties: Vec<Vec<u8>>, owner: T::AccountId) -> DispatchResult {866867 ensure!(properties.len() > 0, "Length of items properties must be greater than 0.");868 let sender = ensure_signed(origin)?;869870 Self::collection_exists(collection_id)?;871 let target_collection = <Collection<T>>::get(collection_id);872873 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;874875 for prop in &properties {876 Self::validate_create_item_args(&target_collection, prop)?;877 }878 for prop in &properties {879 Self::create_item_no_validation(collection_id, &target_collection, prop, &owner)?;880 }881882 Ok(())883 }884885 886 887 888 889 890 891 892 893 894 895 896 897 898 #[weight = T::WeightInfo::burn_item()]899 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {900901 let sender = ensure_signed(origin)?;902 Self::collection_exists(collection_id)?;903904 905 let target_collection = <Collection<T>>::get(collection_id);906 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||907 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),908 "Only item owner, collection owner and admins can modify item");909910 if target_collection.access == AccessMode::WhiteList {911 Self::check_white_list(collection_id, &sender)?;912 }913914 match target_collection.mode915 {916 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,917 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,918 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,919 _ => ()920 };921922 923 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));924925 Ok(())926 }927928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 #[weight = T::WeightInfo::transfer()]952 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {953954 let sender = ensure_signed(origin)?;955956 957 let target_collection = <Collection<T>>::get(collection_id);958 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||959 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),960 "Only item owner, collection owner and admins can modify item");961962 if target_collection.access == AccessMode::WhiteList {963 Self::check_white_list(collection_id, &sender)?;964 Self::check_white_list(collection_id, &recipient)?;965 }966967 match target_collection.mode968 {969 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,970 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,971 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,972 _ => ()973 };974975 Ok(())976 }977978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 #[weight = T::WeightInfo::approve()]994 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {995996 let sender = ensure_signed(origin)?;997998 999 let target_collection = <Collection<T>>::get(collection_id);1000 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1001 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1002 "Only item owner, collection owner and admins can approve");10031004 if target_collection.access == AccessMode::WhiteList {1005 Self::check_white_list(collection_id, &sender)?;1006 Self::check_white_list(collection_id, &approved)?;1007 }10081009 1010 let amount = 100000000;10111012 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1013 if list_exists {10141015 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1016 let item_contains = list.iter().any(|i| i.approved == approved);10171018 if !item_contains {1019 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1020 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1021 }1022 } else {10231024 let mut list = Vec::new();1025 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1026 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1027 }10281029 Ok(())1030 }1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 #[weight = T::WeightInfo::transfer_from()]1052 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10531054 let sender = ensure_signed(origin)?;1055 let mut appoved_transfer = false;10561057 1058 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1059 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1060 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1061 if opt_item.is_some()1062 {1063 appoved_transfer = true;1064 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1065 }1066 }10671068 1069 let target_collection = <Collection<T>>::get(collection_id);1070 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1071 "Only item owner, collection owner and admins can modify items");10721073 if target_collection.access == AccessMode::WhiteList {1074 Self::check_white_list(collection_id, &sender)?;1075 Self::check_white_list(collection_id, &recipient)?;1076 }10771078 1079 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1080 .into_iter().filter(|i| i.approved != sender.clone()).collect();1081 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);108210831084 match target_collection.mode1085 {1086 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1087 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1088 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1089 _ => ()1090 };10911092 Ok(())1093 }10941095 1096 #[weight = 0]1097 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10981099 1100 1101 1102 11031104 11051106 11071108 Ok(())1109 }11101111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 #[weight = T::WeightInfo::set_variable_meta_data()]1124 pub fn set_variable_meta_data (1125 origin,1126 collection_id: u64,1127 item_id: u64,1128 data: Vec<u8>1129 ) -> DispatchResult {1130 let sender = ensure_signed(origin)?;1131 1132 Self::collection_exists(collection_id)?;11331134 1135 let target_collection = <Collection<T>>::get(collection_id);1136 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1137 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1138 "Only item owner, collection owner and admins can modify item");11391140 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11411142 match target_collection.mode1143 {1144 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1145 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1146 _ => ()1147 };11481149 Ok(())1150 }1151 11521153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 #[weight = T::WeightInfo::set_offchain_schema()]1166 pub fn set_offchain_schema(1167 origin,1168 collection_id: u64,1169 schema: Vec<u8>1170 ) -> DispatchResult {1171 let sender = ensure_signed(origin)?;1172 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11731174 let mut target_collection = <Collection<T>>::get(collection_id);1175 target_collection.offchain_schema = schema;1176 <Collection<T>>::insert(collection_id, target_collection);11771178 Ok(())1179 }11801181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 #[weight = T::WeightInfo::set_const_on_chain_schema()]1194 pub fn set_const_on_chain_schema (1195 origin,1196 collection_id: u64,1197 schema: Vec<u8>1198 ) -> DispatchResult {1199 let sender = ensure_signed(origin)?;1200 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12011202 let mut target_collection = <Collection<T>>::get(collection_id);1203 target_collection.const_on_chain_schema = schema;1204 <Collection<T>>::insert(collection_id, target_collection);12051206 Ok(())1207 }12081209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 #[weight = T::WeightInfo::set_const_on_chain_schema()]1222 pub fn set_variable_on_chain_schema (1223 origin,1224 collection_id: u64,1225 schema: Vec<u8>1226 ) -> DispatchResult {1227 let sender = ensure_signed(origin)?;1228 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12291230 let mut target_collection = <Collection<T>>::get(collection_id);1231 target_collection.variable_on_chain_schema = schema;1232 <Collection<T>>::insert(collection_id, target_collection);12331234 Ok(())1235 }12361237 1238 #[weight = 0]1239 pub fn set_chain_limits(1240 origin,1241 limits: ChainLimits1242 ) -> DispatchResult {1243 ensure_root(origin)?;1244 <ChainLimit>::put(limits);1245 Ok(())1246 }12471248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 #[weight = 0]1260 pub fn enable_contract_sponsoring(1261 origin,1262 contract_address: T::AccountId,1263 enable: bool1264 ) -> DispatchResult {1265 let sender = ensure_signed(origin)?;1266 let mut is_owner = false;1267 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1268 let owner = <ContractOwner<T>>::get(&contract_address);1269 is_owner = sender == owner;1270 }1271 ensure!(is_owner, "Only contract owner may call this method");12721273 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1274 Ok(())1275 }12761277 }1278}12791280impl<T: Trait> Module<T> {12811282 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {12831284 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1285 ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1286 Self::check_white_list(collection_id, owner)?;1287 Self::check_white_list(collection_id, sender)?;1288 }12891290 Ok(())1291 }12921293 fn validate_create_item_args(collection: &CollectionType<T::AccountId>, properties: &Vec<u8>) -> DispatchResult {12941295 match collection.mode1296 {1297 CollectionMode::NFT(_) => {12981299 1300 ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1301 },1302 CollectionMode::Fungible(_) => {13031304 1305 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type")1306 },1307 CollectionMode::ReFungible(_, _) => {13081309 1310 ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1311 },1312 _ => {1313 fail!("Unexpected collection mode")1314 }1315 }13161317 Ok(())1318 }13191320 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, properties: &Vec<u8>, owner: &T::AccountId) -> DispatchResult {1321 match collection.mode1322 {1323 CollectionMode::NFT(_) => {13241325 1326 let item = NftItemType {1327 collection: collection_id,1328 owner: owner.clone(),1329 data: properties.clone(),1330 };13311332 Self::add_nft_item(item)?;13331334 },1335 CollectionMode::Fungible(_) => {13361337 let item = FungibleItemType {1338 collection: collection_id,1339 owner: owner.clone(),1340 value: (10 as u128).pow(collection.decimal_points)1341 };13421343 Self::add_fungible_item(item)?;1344 },1345 CollectionMode::ReFungible(_, _) => {13461347 let mut owner_list = Vec::new();1348 let value = (10 as u128).pow(collection.decimal_points);1349 owner_list.push(Ownership {owner: owner.clone(), fraction: value});13501351 let item = ReFungibleItemType {1352 collection: collection_id,1353 owner: owner_list,1354 data: properties.clone()1355 };13561357 Self::add_refungible_item(item)?;1358 },1359 _ => { ensure!(1 == 0,"just error"); }13601361 };13621363 1364 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));13651366 Ok(())1367 }13681369 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1370 let current_index = <ItemListIndex>::get(item.collection)1371 .checked_add(1)1372 .expect("Item list index id error");1373 let itemcopy = item.clone();1374 let owner = item.owner.clone();1375 let value = item.value as u64;13761377 Self::add_token_index(item.collection, current_index, owner.clone())?;13781379 <ItemListIndex>::insert(item.collection, current_index);1380 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13811382 1383 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1384 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1385 1386 1387 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1388 .checked_add(value)1389 .unwrap();1390 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13911392 Ok(())1393 }13941395 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1396 let current_index = <ItemListIndex>::get(item.collection)1397 .checked_add(1)1398 .expect("Item list index id error");1399 let itemcopy = item.clone();14001401 let value = item.owner.first().unwrap().fraction as u64;1402 let owner = item.owner.first().unwrap().owner.clone();14031404 Self::add_token_index(item.collection, current_index, owner.clone())?;14051406 <ItemListIndex>::insert(item.collection, current_index);1407 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14081409 1410 let block_number: T::BlockNumber = 0.into();1411 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14121413 1414 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1415 .checked_add(value)1416 .unwrap();1417 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14181419 Ok(())1420 }14211422 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1423 let current_index = <ItemListIndex>::get(item.collection)1424 .checked_add(1)1425 .expect("Item list index id error");14261427 let item_owner = item.owner.clone();1428 let collection_id = item.collection.clone();1429 Self::add_token_index(collection_id, current_index, item.owner.clone())?;14301431 <ItemListIndex>::insert(collection_id, current_index);1432 <NftItemList<T>>::insert(collection_id, current_index, item);14331434 1435 let block_number: T::BlockNumber = 0.into();1436 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14371438 1439 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1440 .checked_add(1)1441 .unwrap();1442 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14431444 Ok(())1445 }14461447 fn burn_refungible_item(1448 collection_id: u64,1449 item_id: u64,1450 owner: T::AccountId,1451 ) -> DispatchResult {1452 ensure!(1453 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1454 "Item does not exists"1455 );1456 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1457 let item = collection1458 .owner1459 .iter()1460 .filter(|&i| i.owner == owner)1461 .next()1462 .unwrap();1463 Self::remove_token_index(collection_id, item_id, owner.clone())?;14641465 1466 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14671468 1469 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1470 .checked_sub(item.fraction as u64)1471 .unwrap();1472 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14731474 <ReFungibleItemList<T>>::remove(collection_id, item_id);14751476 Ok(())1477 }14781479 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1480 ensure!(1481 <NftItemList<T>>::contains_key(collection_id, item_id),1482 "Item does not exists"1483 );1484 let item = <NftItemList<T>>::get(collection_id, item_id);1485 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14861487 1488 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14891490 1491 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1492 .checked_sub(1)1493 .unwrap();1494 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1495 <NftItemList<T>>::remove(collection_id, item_id);14961497 Ok(())1498 }14991500 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1501 ensure!(1502 <FungibleItemList<T>>::contains_key(collection_id, item_id),1503 "Item does not exists"1504 );1505 let item = <FungibleItemList<T>>::get(collection_id, item_id);1506 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15071508 1509 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15101511 1512 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1513 .checked_sub(item.value as u64)1514 .unwrap();1515 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15161517 <FungibleItemList<T>>::remove(collection_id, item_id);15181519 Ok(())1520 }15211522 fn collection_exists(collection_id: u64) -> DispatchResult {1523 ensure!(1524 <Collection<T>>::contains_key(collection_id),1525 "This collection does not exist"1526 );1527 Ok(())1528 }15291530 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1531 Self::collection_exists(collection_id)?;15321533 let target_collection = <Collection<T>>::get(collection_id);1534 ensure!(1535 subject == target_collection.owner,1536 "You do not own this collection"1537 );15381539 Ok(())1540 }15411542 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1543 let target_collection = <Collection<T>>::get(collection_id);1544 let mut result: bool = subject == target_collection.owner;1545 let exists = <AdminList<T>>::contains_key(collection_id);15461547 if !result & exists {1548 if <AdminList<T>>::get(collection_id).contains(&subject) {1549 result = true1550 }1551 }15521553 result1554 }15551556 fn check_owner_or_admin_permissions(1557 collection_id: u64,1558 subject: T::AccountId,1559 ) -> DispatchResult {1560 Self::collection_exists(collection_id)?;1561 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15621563 ensure!(1564 result,1565 "You do not have permissions to modify this collection"1566 );1567 Ok(())1568 }15691570 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1571 let target_collection = <Collection<T>>::get(collection_id);15721573 match target_collection.mode {1574 CollectionMode::NFT => {1575 <NftItemList<T>>::get(collection_id, item_id).owner == subject1576 }1577 CollectionMode::Fungible(_) => {1578 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1579 }1580 CollectionMode::ReFungible(_) => {1581 <ReFungibleItemList<T>>::get(collection_id, item_id)1582 .owner1583 .iter()1584 .any(|i| i.owner == subject)1585 }1586 CollectionMode::Invalid => false,1587 }1588 }15891590 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1591 let mes = "Address is not in white list";1592 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1593 let wl = <WhiteList<T>>::get(collection_id);1594 ensure!(wl.contains(address), mes);15951596 Ok(())1597 }15981599 fn transfer_fungible(1600 collection_id: u64,1601 item_id: u64,1602 value: u64,1603 owner: T::AccountId,1604 new_owner: T::AccountId,1605 ) -> DispatchResult {1606 ensure!(1607 <FungibleItemList<T>>::contains_key(collection_id, item_id),1608 "Item not exists"1609 );16101611 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1612 let amount = full_item.value;16131614 ensure!(amount >= value.into(), "Item balance not enouth");16151616 1617 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1618 .checked_sub(value)1619 .unwrap();1620 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16211622 let mut new_owner_account_id = 0;1623 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1624 if new_owner_items.len() > 0 {1625 new_owner_account_id = new_owner_items[0];1626 }16271628 let val64 = value.into();16291630 1631 if amount == val64 && new_owner_account_id == 0 {1632 1633 1634 let mut new_full_item = full_item.clone();1635 new_full_item.owner = new_owner.clone();1636 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16371638 1639 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1640 .checked_add(value)1641 .unwrap();1642 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16431644 1645 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1646 } else {1647 let mut new_full_item = full_item.clone();1648 new_full_item.value -= val64;16491650 1651 if new_owner_account_id > 0 {1652 1653 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1654 item.value += val64;16551656 1657 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1658 .checked_add(value)1659 .unwrap();1660 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16611662 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1663 } else {1664 1665 let item = FungibleItemType {1666 collection: collection_id,1667 owner: new_owner.clone(),1668 value: val64,1669 };16701671 Self::add_fungible_item(item)?;1672 }16731674 if amount == val64 {1675 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16761677 1678 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1679 <FungibleItemList<T>>::remove(collection_id, item_id);1680 }16811682 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1683 }16841685 Ok(())1686 }16871688 fn transfer_refungible(1689 collection_id: u64,1690 item_id: u64,1691 value: u64,1692 owner: T::AccountId,1693 new_owner: T::AccountId,1694 ) -> DispatchResult {1695 ensure!(1696 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1697 "Item not exists"1698 );16991700 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1701 let item = full_item1702 .owner1703 .iter()1704 .filter(|i| i.owner == owner)1705 .next()1706 .unwrap();1707 let amount = item.fraction;17081709 ensure!(amount >= value.into(), "Item balance not enouth");17101711 1712 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1713 .checked_sub(value)1714 .unwrap();1715 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17161717 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1718 .checked_add(value)1719 .unwrap();1720 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17211722 let old_owner = item.owner.clone();1723 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1724 let val64 = value.into();17251726 1727 if amount == val64 && !new_owner_has_account {1728 1729 1730 let mut new_full_item = full_item.clone();1731 new_full_item1732 .owner1733 .iter_mut()1734 .find(|i| i.owner == owner)1735 .unwrap()1736 .owner = new_owner.clone();1737 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17381739 1740 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1741 } else {1742 let mut new_full_item = full_item.clone();1743 new_full_item1744 .owner1745 .iter_mut()1746 .find(|i| i.owner == owner)1747 .unwrap()1748 .fraction -= val64;17491750 1751 if new_owner_has_account {1752 1753 new_full_item1754 .owner1755 .iter_mut()1756 .find(|i| i.owner == new_owner)1757 .unwrap()1758 .fraction += val64;1759 } else {1760 1761 new_full_item.owner.push(Ownership {1762 owner: new_owner.clone(),1763 fraction: val64,1764 });1765 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1766 }17671768 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1769 }17701771 Ok(())1772 }17731774 fn transfer_nft(1775 collection_id: u64,1776 item_id: u64,1777 sender: T::AccountId,1778 new_owner: T::AccountId,1779 ) -> DispatchResult {1780 ensure!(1781 <NftItemList<T>>::contains_key(collection_id, item_id),1782 "Item not exists"1783 );17841785 let mut item = <NftItemList<T>>::get(collection_id, item_id);17861787 ensure!(1788 sender == item.owner,1789 "sender parameter and item owner must be equal"1790 );17911792 1793 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1794 .checked_sub(1)1795 .unwrap();1796 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17971798 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1799 .checked_add(1)1800 .unwrap();1801 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18021803 1804 let old_owner = item.owner.clone();1805 item.owner = new_owner.clone();1806 <NftItemList<T>>::insert(collection_id, item_id, item);18071808 1809 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18101811 1812 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1813 Ok(())1814 }1815 1816 fn item_exists(1817 collection_id: u64,1818 item_id: u64,1819 mode: &CollectionMode1820 ) -> DispatchResult {1821 match mode {1822 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1823 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1824 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1825 _ => ()1826 };1827 1828 Ok(())1829 }18301831 fn set_re_fungible_variable_data(1832 collection_id: u64,1833 item_id: u64,1834 data: Vec<u8>1835 ) -> DispatchResult {1836 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18371838 item.variable_data = data;18391840 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18411842 Ok(())1843 }18441845 fn set_nft_variable_data(1846 collection_id: u64,1847 item_id: u64,1848 data: Vec<u8>1849 ) -> DispatchResult {1850 let mut item = <NftItemList<T>>::get(collection_id, item_id);1851 1852 item.variable_data = data;18531854 <NftItemList<T>>::insert(collection_id, item_id, item);1855 1856 Ok(())1857 }18581859 fn init_collection(item: &CollectionType<T::AccountId>) {1860 1861 assert!(1862 item.decimal_points <= 4,1863 "decimal_points parameter must be lower than 4"1864 );1865 assert!(1866 item.name.len() <= 64,1867 "Collection name can not be longer than 63 char"1868 );1869 assert!(1870 item.name.len() <= 256,1871 "Collection description can not be longer than 255 char"1872 );1873 assert!(1874 item.token_prefix.len() <= 16,1875 "Token prefix can not be longer than 15 char"1876 );18771878 1879 let next_id = CreatedCollectionCount::get()1880 .checked_add(1)1881 .expect("collection id error");18821883 CreatedCollectionCount::put(next_id);1884 }18851886 fn init_nft_token(item: &NftItemType<T::AccountId>) {1887 let current_index = <ItemListIndex>::get(item.collection)1888 .checked_add(1)1889 .expect("Item list index id error");18901891 let item_owner = item.owner.clone();1892 let collection_id = item.collection.clone();1893 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18941895 <ItemListIndex>::insert(collection_id, current_index);18961897 1898 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1899 .checked_add(1)1900 .unwrap();1901 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1902 }19031904 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1905 let current_index = <ItemListIndex>::get(item.collection)1906 .checked_add(1)1907 .expect("Item list index id error");1908 let owner = item.owner.clone();1909 let value = item.value as u64;19101911 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19121913 <ItemListIndex>::insert(item.collection, current_index);19141915 1916 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1917 .checked_add(value)1918 .unwrap();1919 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1920 }19211922 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1923 let current_index = <ItemListIndex>::get(item.collection)1924 .checked_add(1)1925 .expect("Item list index id error");19261927 let value = item.owner.first().unwrap().fraction as u64;1928 let owner = item.owner.first().unwrap().owner.clone();19291930 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19311932 <ItemListIndex>::insert(item.collection, current_index);19331934 1935 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1936 .checked_add(value)1937 .unwrap();1938 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1939 }19401941 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19421943 1944 if <AccountItemCount<T>>::contains_key(owner.clone()) {19451946 1947 let count = <AccountItemCount<T>>::get(owner.clone());1948 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");19491950 <AccountItemCount<T>>::insert(owner.clone(), 1951 count.checked_add(1).unwrap());1952 }1953 else {1954 <AccountItemCount<T>>::insert(owner.clone(), 1);1955 }19561957 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1958 if list_exists {1959 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1960 let item_contains = list.contains(&item_index.clone());19611962 if !item_contains {1963 list.push(item_index.clone());1964 }19651966 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1967 } else {1968 let mut itm = Vec::new();1969 itm.push(item_index.clone());1970 <AddressTokens<T>>::insert(collection_id, owner, itm);1971 1972 }19731974 Ok(())1975 }19761977 fn remove_token_index(1978 collection_id: u64,1979 item_index: u64,1980 owner: T::AccountId,1981 ) -> DispatchResult {19821983 1984 <AccountItemCount<T>>::insert(owner.clone(), 1985 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());198619871988 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1989 if list_exists {1990 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1991 let item_contains = list.contains(&item_index.clone());19921993 if item_contains {1994 list.retain(|&item| item != item_index);1995 <AddressTokens<T>>::insert(collection_id, owner, list);1996 }1997 }19981999 Ok(())2000 }20012002 fn move_token_index(2003 collection_id: u64,2004 item_index: u64,2005 old_owner: T::AccountId,2006 new_owner: T::AccountId,2007 ) -> DispatchResult {2008 Self::remove_token_index(collection_id, item_index, old_owner)?;2009 Self::add_token_index(collection_id, item_index, new_owner)?;20102011 Ok(())2012 }2013}2014201520162017201820192020pub type Multiplier = FixedU128;20212022type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2023 <T as system::Trait>::AccountId,2024>>::Balance;2025type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2026 <T as system::Trait>::AccountId,2027>>::NegativeImbalance;2028202920302031#[derive(Encode, Decode, Clone, Eq, PartialEq)]2032pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2033 #[codec(compact)] BalanceOf<T>2034);20352036impl<T: Trait + Send + Sync> sp_std::fmt::Debug2037 for ChargeTransactionPayment<T>2038{2039 #[cfg(feature = "std")]2040 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2041 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2042 }2043 #[cfg(not(feature = "std"))]2044 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2045 Ok(())2046 }2047}20482049impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2050where2051 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2052 BalanceOf<T>: Send + Sync + FixedPointOperand,2053{2054 2055 pub fn from(fee: BalanceOf<T>) -> Self {2056 Self(fee)2057 }20582059 pub fn traditional_fee(2060 len: usize,2061 info: &DispatchInfoOf<T::Call>,2062 tip: BalanceOf<T>,2063 ) -> BalanceOf<T>2064 where2065 T::Call: Dispatchable<Info = DispatchInfo>,2066 {2067 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2068 }20692070 fn withdraw_fee(2071 &self,2072 who: &T::AccountId,2073 call: &T::Call,2074 info: &DispatchInfoOf<T::Call>,2075 len: usize,2076 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2077 let tip = self.0;20782079 2080 2081 2082 2083 2084 2085 2086 let fee = Self::traditional_fee(len, info, tip);20872088 2089 2090 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2091 Some(Call::create_item(collection_id, _properties, _owner)) => {2092 <Collection<T>>::get(collection_id).sponsor2093 }2094 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2095 let _collection_mode = <Collection<T>>::get(collection_id).mode;20962097 2098 let sponsor_transfer = match _collection_mode {2099 CollectionMode::NFT => {2100 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2101 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2102 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2103 if block_number >= limit_time {2104 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2105 true2106 }2107 else {2108 false2109 }2110 }2111 CollectionMode::Fungible(_) => {2112 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2113 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2114 if basket.iter().any(|i| i.address == _new_owner.clone())2115 {2116 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2117 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2118 if block_number >= limit_time {2119 basket.retain(|x| x.address == item.address);2120 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2121 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2122 true2123 }2124 else {2125 false2126 }2127 }2128 else {2129 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2130 true2131 }2132 }2133 CollectionMode::ReFungible(_) => {2134 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2135 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2136 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2137 if block_number >= limit_time {2138 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2139 true2140 } else {2141 false2142 }2143 }2144 _ => {2145 false2146 },2147 };21482149 if !sponsor_transfer {2150 T::AccountId::default()2151 } else {2152 <Collection<T>>::get(collection_id).sponsor2153 }2154 }21552156 _ => T::AccountId::default(),2157 };21582159 2160 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21612162 2163 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21642165 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2166 code_hash,2167 &data,2168 &who,2169 );2170 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21712172 T::AccountId::default()2173 },21742175 2176 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21772178 let mut sp = T::AccountId::default();2179 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2180 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2181 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2182 sp = called_contract;2183 }2184 }21852186 sp2187 },21882189 _ => sponsor,2190 };21912192 let mut who_pays_fee: T::AccountId = sponsor.clone();2193 if sponsor == T::AccountId::default() {2194 who_pays_fee = who.clone();2195 }21962197 2198 if fee.is_zero() {2199 return Ok((fee, None));2200 }22012202 match <T as transaction_payment::Trait>::Currency::withdraw(2203 &who_pays_fee,2204 fee,2205 if tip.is_zero() {2206 WithdrawReason::TransactionPayment.into()2207 } else {2208 WithdrawReason::TransactionPayment | WithdrawReason::Tip2209 },2210 ExistenceRequirement::KeepAlive,2211 ) {2212 Ok(imbalance) => Ok((fee, Some(imbalance))),2213 Err(_) => Err(InvalidTransaction::Payment.into()),2214 }2215 }2216}221722182219impl<T: Trait + Send + Sync> SignedExtension2220 for ChargeTransactionPayment<T>2221where2222 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2223 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2224{2225 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2226 type AccountId = T::AccountId;2227 type Call = T::Call;2228 type AdditionalSigned = ();2229 type Pre = (2230 BalanceOf<T>,2231 Self::AccountId,2232 Option<NegativeImbalanceOf<T>>,2233 BalanceOf<T>,2234 );2235 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2236 Ok(())2237 }22382239 fn validate(2240 &self,2241 _who: &Self::AccountId,2242 _call: &Self::Call,2243 _info: &DispatchInfoOf<Self::Call>,2244 _len: usize,2245 ) -> TransactionValidity {2246 Ok(ValidTransaction::default())2247 }22482249 fn pre_dispatch(2250 self,2251 who: &Self::AccountId,2252 call: &Self::Call,2253 info: &DispatchInfoOf<Self::Call>,2254 len: usize,2255 ) -> Result<Self::Pre, TransactionValidityError> {2256 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2257 Ok((self.0, who.clone(), imbalance, fee))2258 }22592260 fn post_dispatch(2261 pre: Self::Pre,2262 info: &DispatchInfoOf<Self::Call>,2263 post_info: &PostDispatchInfoOf<Self::Call>,2264 len: usize,2265 _result: &DispatchResult,2266 ) -> Result<(), TransactionValidityError> {2267 let (tip, who, imbalance, fee) = pre;2268 if let Some(payed) = imbalance {2269 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2270 len as u32, info, post_info, tip,2271 );2272 let refund = fee.saturating_sub(actual_fee);2273 let actual_payment =2274 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2275 &who, refund,2276 ) {2277 Ok(refund_imbalance) => {2278 2279 2280 match payed.offset(refund_imbalance) {2281 Ok(actual_payment) => actual_payment,2282 Err(_) => return Err(InvalidTransaction::Payment.into()),2283 }2284 }2285 2286 2287 Err(_) => payed,2288 };2289 let imbalances = actual_payment.split(tip);2290 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2291 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2292 );2293 }2294 Ok(())2295 }2296}22972298