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, &data)?;841 Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;842843 Ok(())844 }845846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 #[weight = T::WeightInfo::create_item(items_data.into_iter()865 .map(|data| { data.len() })866 .sum())]867 pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {868869 ensure!(items_data.len() > 0, "Length of items properties must be greater than 0.");870 let sender = ensure_signed(origin)?;871872 Self::collection_exists(collection_id)?;873 let target_collection = <Collection<T>>::get(collection_id);874875 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;876877 for data in &items_data {878 Self::validate_create_item_args(&target_collection, data)?;879 }880 for data in &items_data {881 Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;882 }883884 Ok(())885 }886887 888 889 890 891 892 893 894 895 896 897 898 899 900 #[weight = T::WeightInfo::burn_item()]901 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {902903 let sender = ensure_signed(origin)?;904 Self::collection_exists(collection_id)?;905906 907 let target_collection = <Collection<T>>::get(collection_id);908 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||909 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),910 "Only item owner, collection owner and admins can modify item");911912 if target_collection.access == AccessMode::WhiteList {913 Self::check_white_list(collection_id, &sender)?;914 }915916 match target_collection.mode917 {918 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,919 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,920 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,921 _ => ()922 };923924 925 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));926927 Ok(())928 }929930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 #[weight = T::WeightInfo::transfer()]954 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {955956 let sender = ensure_signed(origin)?;957958 959 let target_collection = <Collection<T>>::get(collection_id);960 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||961 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),962 "Only item owner, collection owner and admins can modify item");963964 if target_collection.access == AccessMode::WhiteList {965 Self::check_white_list(collection_id, &sender)?;966 Self::check_white_list(collection_id, &recipient)?;967 }968969 match target_collection.mode970 {971 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,972 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,973 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,974 _ => ()975 };976977 Ok(())978 }979980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 #[weight = T::WeightInfo::approve()]996 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {997998 let sender = ensure_signed(origin)?;9991000 1001 let target_collection = <Collection<T>>::get(collection_id);1002 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1003 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1004 "Only item owner, collection owner and admins can approve");10051006 if target_collection.access == AccessMode::WhiteList {1007 Self::check_white_list(collection_id, &sender)?;1008 Self::check_white_list(collection_id, &approved)?;1009 }10101011 1012 let amount = 100000000;10131014 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1015 if list_exists {10161017 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1018 let item_contains = list.iter().any(|i| i.approved == approved);10191020 if !item_contains {1021 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1022 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1023 }1024 } else {10251026 let mut list = Vec::new();1027 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1028 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1029 }10301031 Ok(())1032 }1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 #[weight = T::WeightInfo::transfer_from()]1054 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10551056 let sender = ensure_signed(origin)?;1057 let mut appoved_transfer = false;10581059 1060 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1061 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1062 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1063 if opt_item.is_some()1064 {1065 appoved_transfer = true;1066 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1067 }1068 }10691070 1071 let target_collection = <Collection<T>>::get(collection_id);1072 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1073 "Only item owner, collection owner and admins can modify items");10741075 if target_collection.access == AccessMode::WhiteList {1076 Self::check_white_list(collection_id, &sender)?;1077 Self::check_white_list(collection_id, &recipient)?;1078 }10791080 1081 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1082 .into_iter().filter(|i| i.approved != sender.clone()).collect();1083 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);108410851086 match target_collection.mode1087 {1088 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1089 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1090 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1091 _ => ()1092 };10931094 Ok(())1095 }10961097 1098 #[weight = 0]1099 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11001101 1102 1103 1104 11051106 11071108 11091110 Ok(())1111 }11121113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 #[weight = T::WeightInfo::set_variable_meta_data()]1126 pub fn set_variable_meta_data (1127 origin,1128 collection_id: u64,1129 item_id: u64,1130 data: Vec<u8>1131 ) -> DispatchResult {1132 let sender = ensure_signed(origin)?;1133 1134 Self::collection_exists(collection_id)?;11351136 1137 let target_collection = <Collection<T>>::get(collection_id);1138 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1139 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1140 "Only item owner, collection owner and admins can modify item");11411142 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11431144 match target_collection.mode1145 {1146 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1147 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1148 _ => ()1149 };11501151 Ok(())1152 }1153 11541155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 #[weight = T::WeightInfo::set_offchain_schema()]1168 pub fn set_offchain_schema(1169 origin,1170 collection_id: u64,1171 schema: Vec<u8>1172 ) -> DispatchResult {1173 let sender = ensure_signed(origin)?;1174 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11751176 let mut target_collection = <Collection<T>>::get(collection_id);1177 target_collection.offchain_schema = schema;1178 <Collection<T>>::insert(collection_id, target_collection);11791180 Ok(())1181 }11821183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 #[weight = T::WeightInfo::set_const_on_chain_schema()]1196 pub fn set_const_on_chain_schema (1197 origin,1198 collection_id: u64,1199 schema: Vec<u8>1200 ) -> DispatchResult {1201 let sender = ensure_signed(origin)?;1202 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12031204 let mut target_collection = <Collection<T>>::get(collection_id);1205 target_collection.const_on_chain_schema = schema;1206 <Collection<T>>::insert(collection_id, target_collection);12071208 Ok(())1209 }12101211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 #[weight = T::WeightInfo::set_const_on_chain_schema()]1224 pub fn set_variable_on_chain_schema (1225 origin,1226 collection_id: u64,1227 schema: Vec<u8>1228 ) -> DispatchResult {1229 let sender = ensure_signed(origin)?;1230 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12311232 let mut target_collection = <Collection<T>>::get(collection_id);1233 target_collection.variable_on_chain_schema = schema;1234 <Collection<T>>::insert(collection_id, target_collection);12351236 Ok(())1237 }12381239 1240 #[weight = 0]1241 pub fn set_chain_limits(1242 origin,1243 limits: ChainLimits1244 ) -> DispatchResult {1245 ensure_root(origin)?;1246 <ChainLimit>::put(limits);1247 Ok(())1248 }12491250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 #[weight = 0]1262 pub fn enable_contract_sponsoring(1263 origin,1264 contract_address: T::AccountId,1265 enable: bool1266 ) -> DispatchResult {1267 let sender = ensure_signed(origin)?;1268 let mut is_owner = false;1269 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1270 let owner = <ContractOwner<T>>::get(&contract_address);1271 is_owner = sender == owner;1272 }1273 ensure!(is_owner, "Only contract owner may call this method");12741275 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1276 Ok(())1277 }12781279 }1280}12811282impl<T: Trait> Module<T> {12831284 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {12851286 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1287 ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1288 Self::check_white_list(collection_id, owner)?;1289 Self::check_white_list(collection_id, sender)?;1290 }12911292 Ok(())1293 }12941295 fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1296 match target_collection.mode1297 {1298 CollectionMode::NFT => {1299 if let CreateItemData::NFT(data) = data {1300 1301 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1302 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1303 } else {1304 fail!("Not NFT item data used to mint in NFT collection.");1305 }1306 },1307 CollectionMode::Fungible(_) => {1308 if let CreateItemData::Fungible(_) = data {1309 } else {1310 fail!("Not Fungible item data used to mint in Fungible collection.");1311 }1312 },1313 CollectionMode::ReFungible(_) => {1314 if let CreateItemData::ReFungible(data) = data {13151316 1317 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1318 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1319 } else {1320 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1321 }1322 },1323 _ => { fail!("Unexpected collection type."); }1324 };13251326 Ok(())1327 }13281329 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1330 match data1331 {1332 CreateItemData::NFT(data) => {1333 let item = NftItemType {1334 collection: collection_id,1335 owner,1336 const_data: data.const_data,1337 variable_data: data.variable_data1338 };13391340 Self::add_nft_item(item)?;1341 },1342 CreateItemData::Fungible(_) => {1343 let item = FungibleItemType {1344 collection: collection_id,1345 owner,1346 value: (10 as u128).pow(collection.decimal_points)1347 };13481349 Self::add_fungible_item(item)?;1350 },1351 CreateItemData::ReFungible(data) => {1352 let mut owner_list = Vec::new();1353 let value = (10 as u128).pow(collection.decimal_points);1354 owner_list.push(Ownership {owner: owner.clone(), fraction: value});13551356 let item = ReFungibleItemType {1357 collection: collection_id,1358 owner: owner_list,1359 const_data: data.const_data,1360 variable_data: data.variable_data1361 };13621363 Self::add_refungible_item(item)?;1364 }1365 };136613671368 1369 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));13701371 Ok(())1372 }13731374 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1375 let current_index = <ItemListIndex>::get(item.collection)1376 .checked_add(1)1377 .expect("Item list index id error");1378 let itemcopy = item.clone();1379 let owner = item.owner.clone();1380 let value = item.value as u64;13811382 Self::add_token_index(item.collection, current_index, owner.clone())?;13831384 <ItemListIndex>::insert(item.collection, current_index);1385 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13861387 1388 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1389 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1390 1391 1392 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1393 .checked_add(value)1394 .unwrap();1395 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13961397 Ok(())1398 }13991400 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1401 let current_index = <ItemListIndex>::get(item.collection)1402 .checked_add(1)1403 .expect("Item list index id error");1404 let itemcopy = item.clone();14051406 let value = item.owner.first().unwrap().fraction as u64;1407 let owner = item.owner.first().unwrap().owner.clone();14081409 Self::add_token_index(item.collection, current_index, owner.clone())?;14101411 <ItemListIndex>::insert(item.collection, current_index);1412 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14131414 1415 let block_number: T::BlockNumber = 0.into();1416 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14171418 1419 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1420 .checked_add(value)1421 .unwrap();1422 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14231424 Ok(())1425 }14261427 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1428 let current_index = <ItemListIndex>::get(item.collection)1429 .checked_add(1)1430 .expect("Item list index id error");14311432 let item_owner = item.owner.clone();1433 let collection_id = item.collection.clone();1434 Self::add_token_index(collection_id, current_index, item.owner.clone())?;14351436 <ItemListIndex>::insert(collection_id, current_index);1437 <NftItemList<T>>::insert(collection_id, current_index, item);14381439 1440 let block_number: T::BlockNumber = 0.into();1441 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14421443 1444 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1445 .checked_add(1)1446 .unwrap();1447 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14481449 Ok(())1450 }14511452 fn burn_refungible_item(1453 collection_id: u64,1454 item_id: u64,1455 owner: T::AccountId,1456 ) -> DispatchResult {1457 ensure!(1458 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1459 "Item does not exists"1460 );1461 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1462 let item = collection1463 .owner1464 .iter()1465 .filter(|&i| i.owner == owner)1466 .next()1467 .unwrap();1468 Self::remove_token_index(collection_id, item_id, owner.clone())?;14691470 1471 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14721473 1474 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1475 .checked_sub(item.fraction as u64)1476 .unwrap();1477 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14781479 <ReFungibleItemList<T>>::remove(collection_id, item_id);14801481 Ok(())1482 }14831484 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1485 ensure!(1486 <NftItemList<T>>::contains_key(collection_id, item_id),1487 "Item does not exists"1488 );1489 let item = <NftItemList<T>>::get(collection_id, item_id);1490 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14911492 1493 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14941495 1496 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1497 .checked_sub(1)1498 .unwrap();1499 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1500 <NftItemList<T>>::remove(collection_id, item_id);15011502 Ok(())1503 }15041505 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1506 ensure!(1507 <FungibleItemList<T>>::contains_key(collection_id, item_id),1508 "Item does not exists"1509 );1510 let item = <FungibleItemList<T>>::get(collection_id, item_id);1511 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15121513 1514 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15151516 1517 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1518 .checked_sub(item.value as u64)1519 .unwrap();1520 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15211522 <FungibleItemList<T>>::remove(collection_id, item_id);15231524 Ok(())1525 }15261527 fn collection_exists(collection_id: u64) -> DispatchResult {1528 ensure!(1529 <Collection<T>>::contains_key(collection_id),1530 "This collection does not exist"1531 );1532 Ok(())1533 }15341535 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1536 Self::collection_exists(collection_id)?;15371538 let target_collection = <Collection<T>>::get(collection_id);1539 ensure!(1540 subject == target_collection.owner,1541 "You do not own this collection"1542 );15431544 Ok(())1545 }15461547 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1548 let target_collection = <Collection<T>>::get(collection_id);1549 let mut result: bool = subject == target_collection.owner;1550 let exists = <AdminList<T>>::contains_key(collection_id);15511552 if !result & exists {1553 if <AdminList<T>>::get(collection_id).contains(&subject) {1554 result = true1555 }1556 }15571558 result1559 }15601561 fn check_owner_or_admin_permissions(1562 collection_id: u64,1563 subject: T::AccountId,1564 ) -> DispatchResult {1565 Self::collection_exists(collection_id)?;1566 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15671568 ensure!(1569 result,1570 "You do not have permissions to modify this collection"1571 );1572 Ok(())1573 }15741575 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1576 let target_collection = <Collection<T>>::get(collection_id);15771578 match target_collection.mode {1579 CollectionMode::NFT => {1580 <NftItemList<T>>::get(collection_id, item_id).owner == subject1581 }1582 CollectionMode::Fungible(_) => {1583 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1584 }1585 CollectionMode::ReFungible(_) => {1586 <ReFungibleItemList<T>>::get(collection_id, item_id)1587 .owner1588 .iter()1589 .any(|i| i.owner == subject)1590 }1591 CollectionMode::Invalid => false,1592 }1593 }15941595 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1596 let mes = "Address is not in white list";1597 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1598 let wl = <WhiteList<T>>::get(collection_id);1599 ensure!(wl.contains(address), mes);16001601 Ok(())1602 }16031604 fn transfer_fungible(1605 collection_id: u64,1606 item_id: u64,1607 value: u64,1608 owner: T::AccountId,1609 new_owner: T::AccountId,1610 ) -> DispatchResult {1611 ensure!(1612 <FungibleItemList<T>>::contains_key(collection_id, item_id),1613 "Item not exists"1614 );16151616 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1617 let amount = full_item.value;16181619 ensure!(amount >= value.into(), "Item balance not enouth");16201621 1622 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1623 .checked_sub(value)1624 .unwrap();1625 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16261627 let mut new_owner_account_id = 0;1628 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1629 if new_owner_items.len() > 0 {1630 new_owner_account_id = new_owner_items[0];1631 }16321633 let val64 = value.into();16341635 1636 if amount == val64 && new_owner_account_id == 0 {1637 1638 1639 let mut new_full_item = full_item.clone();1640 new_full_item.owner = new_owner.clone();1641 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16421643 1644 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1645 .checked_add(value)1646 .unwrap();1647 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16481649 1650 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1651 } else {1652 let mut new_full_item = full_item.clone();1653 new_full_item.value -= val64;16541655 1656 if new_owner_account_id > 0 {1657 1658 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1659 item.value += val64;16601661 1662 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1663 .checked_add(value)1664 .unwrap();1665 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16661667 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1668 } else {1669 1670 let item = FungibleItemType {1671 collection: collection_id,1672 owner: new_owner.clone(),1673 value: val64,1674 };16751676 Self::add_fungible_item(item)?;1677 }16781679 if amount == val64 {1680 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16811682 1683 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1684 <FungibleItemList<T>>::remove(collection_id, item_id);1685 }16861687 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1688 }16891690 Ok(())1691 }16921693 fn transfer_refungible(1694 collection_id: u64,1695 item_id: u64,1696 value: u64,1697 owner: T::AccountId,1698 new_owner: T::AccountId,1699 ) -> DispatchResult {1700 ensure!(1701 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1702 "Item not exists"1703 );17041705 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1706 let item = full_item1707 .owner1708 .iter()1709 .filter(|i| i.owner == owner)1710 .next()1711 .unwrap();1712 let amount = item.fraction;17131714 ensure!(amount >= value.into(), "Item balance not enouth");17151716 1717 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1718 .checked_sub(value)1719 .unwrap();1720 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17211722 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1723 .checked_add(value)1724 .unwrap();1725 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17261727 let old_owner = item.owner.clone();1728 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1729 let val64 = value.into();17301731 1732 if amount == val64 && !new_owner_has_account {1733 1734 1735 let mut new_full_item = full_item.clone();1736 new_full_item1737 .owner1738 .iter_mut()1739 .find(|i| i.owner == owner)1740 .unwrap()1741 .owner = new_owner.clone();1742 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17431744 1745 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1746 } else {1747 let mut new_full_item = full_item.clone();1748 new_full_item1749 .owner1750 .iter_mut()1751 .find(|i| i.owner == owner)1752 .unwrap()1753 .fraction -= val64;17541755 1756 if new_owner_has_account {1757 1758 new_full_item1759 .owner1760 .iter_mut()1761 .find(|i| i.owner == new_owner)1762 .unwrap()1763 .fraction += val64;1764 } else {1765 1766 new_full_item.owner.push(Ownership {1767 owner: new_owner.clone(),1768 fraction: val64,1769 });1770 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1771 }17721773 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1774 }17751776 Ok(())1777 }17781779 fn transfer_nft(1780 collection_id: u64,1781 item_id: u64,1782 sender: T::AccountId,1783 new_owner: T::AccountId,1784 ) -> DispatchResult {1785 ensure!(1786 <NftItemList<T>>::contains_key(collection_id, item_id),1787 "Item not exists"1788 );17891790 let mut item = <NftItemList<T>>::get(collection_id, item_id);17911792 ensure!(1793 sender == item.owner,1794 "sender parameter and item owner must be equal"1795 );17961797 1798 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1799 .checked_sub(1)1800 .unwrap();1801 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18021803 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1804 .checked_add(1)1805 .unwrap();1806 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18071808 1809 let old_owner = item.owner.clone();1810 item.owner = new_owner.clone();1811 <NftItemList<T>>::insert(collection_id, item_id, item);18121813 1814 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18151816 1817 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1818 Ok(())1819 }1820 1821 fn item_exists(1822 collection_id: u64,1823 item_id: u64,1824 mode: &CollectionMode1825 ) -> DispatchResult {1826 match mode {1827 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1828 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1829 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1830 _ => ()1831 };1832 1833 Ok(())1834 }18351836 fn set_re_fungible_variable_data(1837 collection_id: u64,1838 item_id: u64,1839 data: Vec<u8>1840 ) -> DispatchResult {1841 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18421843 item.variable_data = data;18441845 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18461847 Ok(())1848 }18491850 fn set_nft_variable_data(1851 collection_id: u64,1852 item_id: u64,1853 data: Vec<u8>1854 ) -> DispatchResult {1855 let mut item = <NftItemList<T>>::get(collection_id, item_id);1856 1857 item.variable_data = data;18581859 <NftItemList<T>>::insert(collection_id, item_id, item);1860 1861 Ok(())1862 }18631864 fn init_collection(item: &CollectionType<T::AccountId>) {1865 1866 assert!(1867 item.decimal_points <= 4,1868 "decimal_points parameter must be lower than 4"1869 );1870 assert!(1871 item.name.len() <= 64,1872 "Collection name can not be longer than 63 char"1873 );1874 assert!(1875 item.name.len() <= 256,1876 "Collection description can not be longer than 255 char"1877 );1878 assert!(1879 item.token_prefix.len() <= 16,1880 "Token prefix can not be longer than 15 char"1881 );18821883 1884 let next_id = CreatedCollectionCount::get()1885 .checked_add(1)1886 .expect("collection id error");18871888 CreatedCollectionCount::put(next_id);1889 }18901891 fn init_nft_token(item: &NftItemType<T::AccountId>) {1892 let current_index = <ItemListIndex>::get(item.collection)1893 .checked_add(1)1894 .expect("Item list index id error");18951896 let item_owner = item.owner.clone();1897 let collection_id = item.collection.clone();1898 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18991900 <ItemListIndex>::insert(collection_id, current_index);19011902 1903 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1904 .checked_add(1)1905 .unwrap();1906 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1907 }19081909 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1910 let current_index = <ItemListIndex>::get(item.collection)1911 .checked_add(1)1912 .expect("Item list index id error");1913 let owner = item.owner.clone();1914 let value = item.value as u64;19151916 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19171918 <ItemListIndex>::insert(item.collection, current_index);19191920 1921 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1922 .checked_add(value)1923 .unwrap();1924 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1925 }19261927 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1928 let current_index = <ItemListIndex>::get(item.collection)1929 .checked_add(1)1930 .expect("Item list index id error");19311932 let value = item.owner.first().unwrap().fraction as u64;1933 let owner = item.owner.first().unwrap().owner.clone();19341935 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19361937 <ItemListIndex>::insert(item.collection, current_index);19381939 1940 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1941 .checked_add(value)1942 .unwrap();1943 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1944 }19451946 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19471948 1949 if <AccountItemCount<T>>::contains_key(owner.clone()) {19501951 1952 let count = <AccountItemCount<T>>::get(owner.clone());1953 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");19541955 <AccountItemCount<T>>::insert(owner.clone(), 1956 count.checked_add(1).unwrap());1957 }1958 else {1959 <AccountItemCount<T>>::insert(owner.clone(), 1);1960 }19611962 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1963 if list_exists {1964 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1965 let item_contains = list.contains(&item_index.clone());19661967 if !item_contains {1968 list.push(item_index.clone());1969 }19701971 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1972 } else {1973 let mut itm = Vec::new();1974 itm.push(item_index.clone());1975 <AddressTokens<T>>::insert(collection_id, owner, itm);1976 1977 }19781979 Ok(())1980 }19811982 fn remove_token_index(1983 collection_id: u64,1984 item_index: u64,1985 owner: T::AccountId,1986 ) -> DispatchResult {19871988 1989 <AccountItemCount<T>>::insert(owner.clone(), 1990 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());199119921993 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1994 if list_exists {1995 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1996 let item_contains = list.contains(&item_index.clone());19971998 if item_contains {1999 list.retain(|&item| item != item_index);2000 <AddressTokens<T>>::insert(collection_id, owner, list);2001 }2002 }20032004 Ok(())2005 }20062007 fn move_token_index(2008 collection_id: u64,2009 item_index: u64,2010 old_owner: T::AccountId,2011 new_owner: T::AccountId,2012 ) -> DispatchResult {2013 Self::remove_token_index(collection_id, item_index, old_owner)?;2014 Self::add_token_index(collection_id, item_index, new_owner)?;20152016 Ok(())2017 }2018}2019202020212022202320242025pub type Multiplier = FixedU128;20262027type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2028 <T as system::Trait>::AccountId,2029>>::Balance;2030type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2031 <T as system::Trait>::AccountId,2032>>::NegativeImbalance;2033203420352036#[derive(Encode, Decode, Clone, Eq, PartialEq)]2037pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2038 #[codec(compact)] BalanceOf<T>2039);20402041impl<T: Trait + Send + Sync> sp_std::fmt::Debug2042 for ChargeTransactionPayment<T>2043{2044 #[cfg(feature = "std")]2045 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2046 write!(f, "ChargeTransactionPayment<{:?}>", self.0)2047 }2048 #[cfg(not(feature = "std"))]2049 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2050 Ok(())2051 }2052}20532054impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2055where2056 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2057 BalanceOf<T>: Send + Sync + FixedPointOperand,2058{2059 2060 pub fn from(fee: BalanceOf<T>) -> Self {2061 Self(fee)2062 }20632064 pub fn traditional_fee(2065 len: usize,2066 info: &DispatchInfoOf<T::Call>,2067 tip: BalanceOf<T>,2068 ) -> BalanceOf<T>2069 where2070 T::Call: Dispatchable<Info = DispatchInfo>,2071 {2072 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2073 }20742075 fn withdraw_fee(2076 &self,2077 who: &T::AccountId,2078 call: &T::Call,2079 info: &DispatchInfoOf<T::Call>,2080 len: usize,2081 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2082 let tip = self.0;20832084 2085 2086 2087 2088 2089 2090 2091 let fee = Self::traditional_fee(len, info, tip);20922093 2094 2095 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2096 Some(Call::create_item(collection_id, _properties, _owner)) => {2097 <Collection<T>>::get(collection_id).sponsor2098 }2099 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2100 let _collection_mode = <Collection<T>>::get(collection_id).mode;21012102 2103 let sponsor_transfer = match _collection_mode {2104 CollectionMode::NFT => {2105 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2106 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2107 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2108 if block_number >= limit_time {2109 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2110 true2111 }2112 else {2113 false2114 }2115 }2116 CollectionMode::Fungible(_) => {2117 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2118 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2119 if basket.iter().any(|i| i.address == _new_owner.clone())2120 {2121 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2122 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2123 if block_number >= limit_time {2124 basket.retain(|x| x.address == item.address);2125 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2126 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2127 true2128 }2129 else {2130 false2131 }2132 }2133 else {2134 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2135 true2136 }2137 }2138 CollectionMode::ReFungible(_) => {2139 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2140 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2141 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2142 if block_number >= limit_time {2143 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2144 true2145 } else {2146 false2147 }2148 }2149 _ => {2150 false2151 },2152 };21532154 if !sponsor_transfer {2155 T::AccountId::default()2156 } else {2157 <Collection<T>>::get(collection_id).sponsor2158 }2159 }21602161 _ => T::AccountId::default(),2162 };21632164 2165 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21662167 2168 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21692170 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2171 code_hash,2172 &data,2173 &who,2174 );2175 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21762177 T::AccountId::default()2178 },21792180 2181 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21822183 let mut sp = T::AccountId::default();2184 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2185 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2186 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2187 sp = called_contract;2188 }2189 }21902191 sp2192 },21932194 _ => sponsor,2195 };21962197 let mut who_pays_fee: T::AccountId = sponsor.clone();2198 if sponsor == T::AccountId::default() {2199 who_pays_fee = who.clone();2200 }22012202 2203 if fee.is_zero() {2204 return Ok((fee, None));2205 }22062207 match <T as transaction_payment::Trait>::Currency::withdraw(2208 &who_pays_fee,2209 fee,2210 if tip.is_zero() {2211 WithdrawReason::TransactionPayment.into()2212 } else {2213 WithdrawReason::TransactionPayment | WithdrawReason::Tip2214 },2215 ExistenceRequirement::KeepAlive,2216 ) {2217 Ok(imbalance) => Ok((fee, Some(imbalance))),2218 Err(_) => Err(InvalidTransaction::Payment.into()),2219 }2220 }2221}222222232224impl<T: Trait + Send + Sync> SignedExtension2225 for ChargeTransactionPayment<T>2226where2227 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2228 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2229{2230 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2231 type AccountId = T::AccountId;2232 type Call = T::Call;2233 type AdditionalSigned = ();2234 type Pre = (2235 BalanceOf<T>,2236 Self::AccountId,2237 Option<NegativeImbalanceOf<T>>,2238 BalanceOf<T>,2239 );2240 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2241 Ok(())2242 }22432244 fn validate(2245 &self,2246 _who: &Self::AccountId,2247 _call: &Self::Call,2248 _info: &DispatchInfoOf<Self::Call>,2249 _len: usize,2250 ) -> TransactionValidity {2251 Ok(ValidTransaction::default())2252 }22532254 fn pre_dispatch(2255 self,2256 who: &Self::AccountId,2257 call: &Self::Call,2258 info: &DispatchInfoOf<Self::Call>,2259 len: usize,2260 ) -> Result<Self::Pre, TransactionValidityError> {2261 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2262 Ok((self.0, who.clone(), imbalance, fee))2263 }22642265 fn post_dispatch(2266 pre: Self::Pre,2267 info: &DispatchInfoOf<Self::Call>,2268 post_info: &PostDispatchInfoOf<Self::Call>,2269 len: usize,2270 _result: &DispatchResult,2271 ) -> Result<(), TransactionValidityError> {2272 let (tip, who, imbalance, fee) = pre;2273 if let Some(payed) = imbalance {2274 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2275 len as u32, info, post_info, tip,2276 );2277 let refund = fee.saturating_sub(actual_fee);2278 let actual_payment =2279 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2280 &who, refund,2281 ) {2282 Ok(refund_imbalance) => {2283 2284 2285 match payed.offset(refund_imbalance) {2286 Ok(actual_payment) => actual_payment,2287 Err(_) => return Err(InvalidTransaction::Payment.into()),2288 }2289 }2290 2291 2292 Err(_) => payed,2293 };2294 let imbalances = actual_payment.split(tip);2295 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2296 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2297 );2298 }2299 Ok(())2300 }2301}23022303