1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use serde::*;56use codec::{Decode, Encode};7pub use frame_support::{8 construct_runtime, decl_event, decl_module, decl_storage,9 dispatch::DispatchResult,10 ensure, parameter_types,11 traits::{12 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,13 Randomness, WithdrawReason,14 },15 weights::{16 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},17 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,18 WeightToFeePolynomial,19 },20 IsSubType, StorageValue,21};2223use frame_system::{self as system, ensure_signed};24use sp_runtime::sp_std::prelude::Vec;25use sp_runtime::{26 traits::{27 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,28 SignedExtension, Zero,29 },30 transaction_validity::{31 InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,32 ValidTransaction,33 },34 FixedPointOperand, FixedU128,35};3637#[cfg(test)]38mod mock;3940#[cfg(test)]41mod tests;4243444546#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]47#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]48pub enum CollectionMode {49 Invalid,50 51 NFT(u32),52 53 Fungible(u32),54 55 ReFungible(u32, u32),56}5758impl Into<u8> for CollectionMode {59 fn into(self) -> u8 {60 match self {61 CollectionMode::Invalid => 0,62 CollectionMode::NFT(_) => 1,63 CollectionMode::Fungible(_) => 2,64 CollectionMode::ReFungible(_, _) => 3,65 }66 }67}6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum AccessMode {72 Normal,73 WhiteList,74}75impl Default for AccessMode {76 fn default() -> Self {77 Self::Normal78 }79}8081impl Default for CollectionMode {82 fn default() -> Self {83 Self::Invalid84 }85}8687#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]88#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]89pub struct Ownership<AccountId> {90 pub owner: AccountId,91 pub fraction: u128,92}9394#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub struct CollectionType<AccountId> {97 pub owner: AccountId,98 pub mode: CollectionMode,99 pub access: AccessMode,100 pub decimal_points: u32,101 pub name: Vec<u16>, 102 pub description: Vec<u16>, 103 pub token_prefix: Vec<u8>, 104 pub custom_data_size: u32,105 pub mint_mode: bool,106 pub offchain_schema: Vec<u8>,107 pub sponsor: AccountId, 108 pub unconfirmed_sponsor: AccountId, 109}110111#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub struct CollectionAdminsType<AccountId> {114 pub admin: AccountId,115 pub collection_id: u64,116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct NftItemType<AccountId> {121 pub collection: u64,122 pub owner: AccountId,123 pub data: Vec<u8>,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct FungibleItemType<AccountId> {129 pub collection: u64,130 pub owner: AccountId,131 pub value: u128,132}133134#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub struct ReFungibleItemType<AccountId> {137 pub collection: u64,138 pub owner: Vec<Ownership<AccountId>>,139 pub data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145 pub approved: AccountId,146 pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152 pub sender: AccountId,153 pub recipient: AccountId,154 pub collection_id: u64,155 pub item_id: u64,156 pub amount: u64,157 pub vesting_date: Moment,158}159160pub trait Trait: system::Trait {161 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;162}163164165166decl_storage! {167 trait Store for Module<T: Trait> as Nft {168169 170 NextCollectionID: u64;171 CreatedCollectionCount: u64;172 ChainVersion: u64;173 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;174175 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;176 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;177 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;178179 180 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;181182 183 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;184185 186 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;187 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;188 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;189190 191 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;192193 194 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;195 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;196 }197 add_extra_genesis {198 build(|config: &GenesisConfig<T>| {199 200 for (_num, _c) in &config.collection {201 <Module<T>>::init_collection(_c);202 }203204 for (_num, _q, _i) in &config.nft_item_id {205 <Module<T>>::init_nft_token(_i);206 }207208 for (_num, _q, _i) in &config.fungible_item_id {209 <Module<T>>::init_fungible_token(_i);210 }211212 for (_num, _q, _i) in &config.refungible_item_id {213 <Module<T>>::init_refungible_token(_i);214 }215 })216 }217}218219decl_event!(220 pub enum Event<T>221 where222 AccountId = <T as system::Trait>::AccountId,223 {224 Created(u64, u8, AccountId),225 ItemCreated(u64, u64),226 ItemDestroyed(u64, u64),227 }228);229230decl_module! {231 pub struct Module<T: Trait> for enum Call where origin: T::Origin {232233 fn deposit_event() = default;234235 fn on_initialize(now: T::BlockNumber) -> Weight {236237 if ChainVersion::get() < 2238 {239 let value = NextCollectionID::get();240 CreatedCollectionCount::put(value);241 ChainVersion::put(2);242 }243244 0245 }246247 248 249 250 251 #[weight = 0]252 pub fn create_collection(origin,253 collection_name: Vec<u16>,254 collection_description: Vec<u16>,255 token_prefix: Vec<u8>,256 mode: CollectionMode) -> DispatchResult {257258 259 let who = ensure_signed(origin)?;260 let custom_data_size = match mode {261 CollectionMode::NFT(size) => size,262 CollectionMode::ReFungible(size, _) => size,263 _ => 0264 };265266 let decimal_points = match mode {267 CollectionMode::Fungible(points) => points,268 CollectionMode::ReFungible(_, points) => points,269 _ => 0270 };271272 273 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");274275 let mut name = collection_name.to_vec();276 name.push(0);277 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");278279 let mut description = collection_description.to_vec();280 description.push(0);281 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");282283 let mut prefix = token_prefix.to_vec();284 prefix.push(0);285 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");286287 288 let next_id = CreatedCollectionCount::get()289 .checked_add(1)290 .expect("collection id error");291292 CreatedCollectionCount::put(next_id);293294 295 let new_collection = CollectionType {296 owner: who.clone(),297 name: name,298 mode: mode.clone(),299 mint_mode: false,300 access: AccessMode::Normal,301 description: description,302 decimal_points: decimal_points,303 token_prefix: prefix,304 offchain_schema: Vec::new(),305 custom_data_size: custom_data_size,306 sponsor: T::AccountId::default(),307 unconfirmed_sponsor: T::AccountId::default(),308 };309310 311 <Collection<T>>::insert(next_id, new_collection);312313 314 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));315316 Ok(())317 }318319 #[weight = 0]320 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {321322 let sender = ensure_signed(origin)?;323 Self::check_owner_permissions(collection_id, sender)?;324325 326 <AddressTokens<T>>::remove_prefix(collection_id);327 <ApprovedList<T>>::remove_prefix(collection_id);328 <Balance<T>>::remove_prefix(collection_id);329 <ItemListIndex>::remove(collection_id);330 <AdminList<T>>::remove(collection_id);331 <Collection<T>>::remove(collection_id);332 <WhiteList<T>>::remove(collection_id);333334 Ok(())335 }336337 #[weight = 0]338 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{339340 let sender = ensure_signed(origin)?;341 Self::check_owner_or_admin_permissions(collection_id, sender)?;342343 let mut white_list_collection: Vec<T::AccountId>;344 if <WhiteList<T>>::contains_key(collection_id) {345 white_list_collection = <WhiteList<T>>::get(collection_id);346 if !white_list_collection.contains(&address.clone())347 {348 white_list_collection.push(address.clone());349 }350 }351 else {352 white_list_collection = Vec::new();353 white_list_collection.push(address.clone());354 }355356 <WhiteList<T>>::insert(collection_id, white_list_collection);357 Ok(())358 }359360 #[weight = 0]361 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{362363 let sender = ensure_signed(origin)?;364 Self::check_owner_or_admin_permissions(collection_id, sender)?;365366 if <WhiteList<T>>::contains_key(collection_id) {367 let mut white_list_collection = <WhiteList<T>>::get(collection_id);368 if white_list_collection.contains(&address.clone())369 {370 white_list_collection.retain(|i| *i != address.clone());371 <WhiteList<T>>::insert(collection_id, white_list_collection);372 }373 }374375 Ok(())376 }377378 #[weight = 0]379 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult380 {381 let sender = ensure_signed(origin)?;382383 Self::check_owner_permissions(collection_id, sender)?;384 let mut target_collection = <Collection<T>>::get(collection_id);385 target_collection.access = mode;386 <Collection<T>>::insert(collection_id, target_collection);387388 Ok(())389 }390391 #[weight = 0]392 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult393 {394 let sender = ensure_signed(origin)?;395396 Self::check_owner_permissions(collection_id, sender)?;397 let mut target_collection = <Collection<T>>::get(collection_id);398 target_collection.mint_mode = mint_permission;399 <Collection<T>>::insert(collection_id, target_collection);400401 Ok(())402 }403404 #[weight = 0]405 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {406407 let sender = ensure_signed(origin)?;408 Self::check_owner_permissions(collection_id, sender)?;409 let mut target_collection = <Collection<T>>::get(collection_id);410 target_collection.owner = new_owner;411 <Collection<T>>::insert(collection_id, target_collection);412413 Ok(())414 }415416 #[weight = 0]417 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {418419 let sender = ensure_signed(origin)?;420 Self::check_owner_or_admin_permissions(collection_id, sender)?;421 let mut admin_arr: Vec<T::AccountId> = Vec::new();422423 if <AdminList<T>>::contains_key(collection_id)424 {425 admin_arr = <AdminList<T>>::get(collection_id);426 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");427 }428429 admin_arr.push(new_admin_id);430 <AdminList<T>>::insert(collection_id, admin_arr);431432 Ok(())433 }434435 #[weight = 0]436 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {437438 let sender = ensure_signed(origin)?;439 Self::check_owner_or_admin_permissions(collection_id, sender)?;440441 if <AdminList<T>>::contains_key(collection_id)442 {443 let mut admin_arr = <AdminList<T>>::get(collection_id);444 admin_arr.retain(|i| *i != account_id);445 <AdminList<T>>::insert(collection_id, admin_arr);446 }447448 Ok(())449 }450451 #[weight = 0]452 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {453454 let sender = ensure_signed(origin)?;455 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");456457 let mut target_collection = <Collection<T>>::get(collection_id);458 ensure!(sender == target_collection.owner, "You do not own this collection");459460 target_collection.unconfirmed_sponsor = new_sponsor;461 <Collection<T>>::insert(collection_id, target_collection);462463 Ok(())464 }465466 #[weight = 0]467 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {468469 let sender = ensure_signed(origin)?;470 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");471472 let mut target_collection = <Collection<T>>::get(collection_id);473 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");474475 target_collection.sponsor = target_collection.unconfirmed_sponsor;476 target_collection.unconfirmed_sponsor = T::AccountId::default();477 <Collection<T>>::insert(collection_id, target_collection);478479 Ok(())480 }481482 #[weight = 0]483 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {484485 let sender = ensure_signed(origin)?;486 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");487488 let mut target_collection = <Collection<T>>::get(collection_id);489 ensure!(sender == target_collection.owner, "You do not own this collection");490491 target_collection.sponsor = T::AccountId::default();492 <Collection<T>>::insert(collection_id, target_collection);493494 Ok(())495 }496497 #[weight = 0]498 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {499500 let sender = ensure_signed(origin)?;501 Self::collection_exists(collection_id)?;502 let target_collection = <Collection<T>>::get(collection_id);503504 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {505 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");506 Self::check_white_list(collection_id, &owner)?;507 Self::check_white_list(collection_id, &sender)?;508 }509510 match target_collection.mode511 {512 CollectionMode::NFT(_) => {513514 515 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");516517 518 let item = NftItemType {519 collection: collection_id,520 owner: owner,521 data: properties.clone(),522 };523524 Self::add_nft_item(item)?;525526 },527 CollectionMode::Fungible(_) => {528529 530 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");531532 let item = FungibleItemType {533 collection: collection_id,534 owner: owner,535 value: (10 as u128).pow(target_collection.decimal_points)536 };537538 Self::add_fungible_item(item)?;539 },540 CollectionMode::ReFungible(_, _) => {541542 543 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");544545 let mut owner_list = Vec::new();546 let value = (10 as u128).pow(target_collection.decimal_points);547 owner_list.push(Ownership {owner: owner.clone(), fraction: value});548549 let item = ReFungibleItemType {550 collection: collection_id,551 owner: owner_list,552 data: properties.clone()553 };554555 Self::add_refungible_item(item)?;556 },557 _ => { ensure!(1 == 0,"just error"); }558559 };560561 562 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));563564 Ok(())565 }566567 #[weight = 0]568 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {569570 let sender = ensure_signed(origin)?;571 Self::collection_exists(collection_id)?;572573 574 let target_collection = <Collection<T>>::get(collection_id);575 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 576 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 577 "Only item owner, collection owner and admins can modify item");578579 if target_collection.access == AccessMode::WhiteList {580 Self::check_white_list(collection_id, &sender)?;581 }582583 match target_collection.mode584 {585 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,586 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,587 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,588 _ => ()589 };590591 592 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));593594 Ok(())595 }596597 #[weight = 0]598 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {599600 let sender = ensure_signed(origin)?;601602 603 let target_collection = <Collection<T>>::get(collection_id);604 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 605 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 606 "Only item owner, collection owner and admins can modify item");607608 if target_collection.access == AccessMode::WhiteList {609 Self::check_white_list(collection_id, &sender)?;610 Self::check_white_list(collection_id, &recipient)?;611 }612613 match target_collection.mode614 {615 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,616 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,617 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,618 _ => ()619 };620621 Ok(())622 }623624 #[weight = 0]625 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {626627 let sender = ensure_signed(origin)?;628629 630 let target_collection = <Collection<T>>::get(collection_id);631 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 632 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 633 "Only item owner, collection owner and admins can approve");634635 if target_collection.access == AccessMode::WhiteList {636 Self::check_white_list(collection_id, &sender)?;637 Self::check_white_list(collection_id, &approved)?;638 }639640 641 let amount = 100000000;642643 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));644 if list_exists {645646 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));647 let item_contains = list.iter().any(|i| i.approved == approved);648649 if !item_contains {650 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });651 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);652 }653 } else {654655 let mut list = Vec::new();656 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });657 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);658 }659660 Ok(())661 }662663 #[weight = 0]664 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {665666 let sender = ensure_signed(origin)?;667 let mut appoved_transfer = false;668669 670 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {671 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));672 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());673 appoved_transfer = opt_item.is_some();674 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");675 }676677 678 let target_collection = <Collection<T>>::get(collection_id);679 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 680 "Only item owner, collection owner and admins can modify items");681682 if target_collection.access == AccessMode::WhiteList {683 Self::check_white_list(collection_id, &sender)?;684 Self::check_white_list(collection_id, &recipient)?;685 }686687 688 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))689 .into_iter().filter(|i| i.approved != sender.clone()).collect();690 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);691692693 match target_collection.mode694 {695 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,696 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,697 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,698 _ => ()699 };700701 Ok(())702 }703704 #[weight = 0]705 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {706707 708 709 710 711712 713714 715716 Ok(())717 }718719 #[weight = 0]720 pub fn set_offchain_schema(721 origin,722 collection_id: u64,723 schema: Vec<u8>724 ) -> DispatchResult {725 let sender = ensure_signed(origin)?;726 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;727728 let mut target_collection = <Collection<T>>::get(collection_id);729 target_collection.offchain_schema = schema;730 <Collection<T>>::insert(collection_id, target_collection);731732 Ok(())733 }734 }735}736737impl<T: Trait> Module<T> {738 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {739 let current_index = <ItemListIndex>::get(item.collection)740 .checked_add(1)741 .expect("Item list index id error");742 let itemcopy = item.clone();743 let owner = item.owner.clone();744 let value = item.value as u64;745746 Self::add_token_index(item.collection, current_index, owner.clone())?;747748 <ItemListIndex>::insert(item.collection, current_index);749 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);750751 752 let new_balance = <Balance<T>>::get(item.collection, owner.clone())753 .checked_add(value)754 .unwrap();755 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);756757 Ok(())758 }759760 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {761 let current_index = <ItemListIndex>::get(item.collection)762 .checked_add(1)763 .expect("Item list index id error");764 let itemcopy = item.clone();765766 let value = item.owner.first().unwrap().fraction as u64;767 let owner = item.owner.first().unwrap().owner.clone();768769 Self::add_token_index(item.collection, current_index, owner.clone())?;770771 <ItemListIndex>::insert(item.collection, current_index);772 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);773774 775 let new_balance = <Balance<T>>::get(item.collection, owner.clone())776 .checked_add(value)777 .unwrap();778 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);779780 Ok(())781 }782783 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {784 let current_index = <ItemListIndex>::get(item.collection)785 .checked_add(1)786 .expect("Item list index id error");787788 let item_owner = item.owner.clone();789 let collection_id = item.collection.clone();790 Self::add_token_index(collection_id, current_index, item.owner.clone())?;791792 <ItemListIndex>::insert(collection_id, current_index);793 <NftItemList<T>>::insert(collection_id, current_index, item);794795 796 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())797 .checked_add(1)798 .unwrap();799 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);800801 Ok(())802 }803804 fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {805 ensure!(806 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),807 "Item does not exists"808 );809 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);810 let item = collection811 .owner812 .iter()813 .filter(|&i| i.owner == owner)814 .next()815 .unwrap();816 Self::remove_token_index(collection_id, item_id, owner.clone())?;817818 819 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));820821 822 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())823 .checked_sub(item.fraction as u64)824 .unwrap();825 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);826827 <ReFungibleItemList<T>>::remove(collection_id, item_id);828829 Ok(())830 }831832 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {833 ensure!(834 <NftItemList<T>>::contains_key(collection_id, item_id),835 "Item does not exists"836 );837 let item = <NftItemList<T>>::get(collection_id, item_id);838 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;839840 841 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));842843 844 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())845 .checked_sub(1)846 .unwrap();847 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);848 <NftItemList<T>>::remove(collection_id, item_id);849850 Ok(())851 }852853 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {854 ensure!(855 <FungibleItemList<T>>::contains_key(collection_id, item_id),856 "Item does not exists"857 );858 let item = <FungibleItemList<T>>::get(collection_id, item_id);859 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;860861 862 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));863864 865 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())866 .checked_sub(item.value as u64)867 .unwrap();868 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);869870 <FungibleItemList<T>>::remove(collection_id, item_id);871872 Ok(())873 }874875 fn collection_exists(collection_id: u64) -> DispatchResult {876 ensure!(877 <Collection<T>>::contains_key(collection_id),878 "This collection does not exist"879 );880 Ok(())881 }882883 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {884 Self::collection_exists(collection_id)?;885886 let target_collection = <Collection<T>>::get(collection_id);887 ensure!(888 subject == target_collection.owner,889 "You do not own this collection"890 );891892 Ok(())893 }894895 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {896897 let target_collection = <Collection<T>>::get(collection_id);898 let mut result: bool = subject == target_collection.owner;899 let exists = <AdminList<T>>::contains_key(collection_id);900901 if !result & exists {902 if <AdminList<T>>::get(collection_id).contains(&subject) {903 result = true904 }905 }906907 result908 }909910 fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {911 912 Self::collection_exists(collection_id)?;913 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());914915 ensure!(result, "You do not have permissions to modify this collection");916 Ok(())917 }918919 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {920 let target_collection = <Collection<T>>::get(collection_id);921922 match target_collection.mode {923 CollectionMode::NFT(_) => {924 <NftItemList<T>>::get(collection_id, item_id).owner == subject925 }926 CollectionMode::Fungible(_) => {927 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject928 }929 CollectionMode::ReFungible(_, _) => {930 <ReFungibleItemList<T>>::get(collection_id, item_id)931 .owner932 .iter()933 .any(|i| i.owner == subject)934 }935 CollectionMode::Invalid => false,936 }937 }938939 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {940941 let mes = "Address is not in white list";942 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);943 let wl = <WhiteList<T>>::get(collection_id);944 ensure!(wl.contains(address), mes);945946 Ok(())947 }948949 fn transfer_fungible(950 collection_id: u64,951 item_id: u64,952 value: u64,953 owner: T::AccountId,954 new_owner: T::AccountId,955 ) -> DispatchResult {956957 ensure!(958 <FungibleItemList<T>>::contains_key(collection_id, item_id),959 "Item not exists"960 );961962 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);963 let amount = full_item.value;964965 ensure!(amount >= value.into(), "Item balance not enouth");966967 968 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())969 .checked_sub(value)970 .unwrap();971 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);972973 let mut new_owner_account_id = 0;974 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());975 if new_owner_items.len() > 0 {976 new_owner_account_id = new_owner_items[0];977 }978979 let val64 = value.into();980981 982 if amount == val64 && new_owner_account_id == 0 {983 984 985 let mut new_full_item = full_item.clone();986 new_full_item.owner = new_owner.clone();987 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);988989 990 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())991 .checked_add(value)992 .unwrap();993 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);994995 996 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;997 } else {998 let mut new_full_item = full_item.clone();999 new_full_item.value -= val64;10001001 1002 if new_owner_account_id > 0 {1003 1004 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1005 item.value += val64;10061007 1008 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1009 .checked_add(value)1010 .unwrap();1011 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);10121013 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1014 } else {1015 1016 let item = FungibleItemType {1017 collection: collection_id,1018 owner: new_owner.clone(),1019 value: val64,1020 };10211022 Self::add_fungible_item(item)?;1023 }10241025 if amount == val64 {1026 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;10271028 1029 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1030 <FungibleItemList<T>>::remove(collection_id, item_id);1031 }10321033 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1034 }10351036 Ok(())1037 }10381039 fn transfer_refungible(1040 collection_id: u64,1041 item_id: u64,1042 value: u64,1043 owner: T::AccountId,1044 new_owner: T::AccountId,1045 ) -> DispatchResult {10461047 ensure!(1048 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1049 "Item not exists"1050 );10511052 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1053 let item = full_item1054 .owner1055 .iter()1056 .filter(|i| i.owner == owner)1057 .next()1058 .unwrap();1059 let amount = item.fraction;10601061 ensure!(amount >= value.into(), "Item balance not enouth");10621063 1064 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1065 .checked_sub(value)1066 .unwrap();1067 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);10681069 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1070 .checked_add(value)1071 .unwrap();1072 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);10731074 let old_owner = item.owner.clone();1075 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1076 let val64 = value.into();10771078 1079 if amount == val64 && !new_owner_has_account {1080 1081 1082 let mut new_full_item = full_item.clone();1083 new_full_item1084 .owner1085 .iter_mut()1086 .find(|i| i.owner == owner)1087 .unwrap()1088 .owner = new_owner.clone();1089 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);10901091 1092 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1093 } else {1094 let mut new_full_item = full_item.clone();1095 new_full_item1096 .owner1097 .iter_mut()1098 .find(|i| i.owner == owner)1099 .unwrap()1100 .fraction -= val64;11011102 1103 if new_owner_has_account {1104 1105 new_full_item1106 .owner1107 .iter_mut()1108 .find(|i| i.owner == new_owner)1109 .unwrap()1110 .fraction += val64;1111 } else {1112 1113 new_full_item.owner.push(Ownership {1114 owner: new_owner.clone(),1115 fraction: val64,1116 });1117 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1118 }11191120 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1121 }11221123 Ok(())1124 }11251126 fn transfer_nft(1127 collection_id: u64,1128 item_id: u64,1129 sender: T::AccountId,1130 new_owner: T::AccountId,1131 ) -> DispatchResult {1132 1133 ensure!(1134 <NftItemList<T>>::contains_key(collection_id, item_id),1135 "Item not exists"1136 );11371138 let mut item = <NftItemList<T>>::get(collection_id, item_id);11391140 ensure!(1141 sender == item.owner,1142 "sender parameter and item owner must be equal"1143 );11441145 1146 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1147 .checked_sub(1)1148 .unwrap();1149 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);11501151 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1152 .checked_add(1)1153 .unwrap();1154 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);11551156 1157 let old_owner = item.owner.clone();1158 item.owner = new_owner.clone();1159 <NftItemList<T>>::insert(collection_id, item_id, item);11601161 1162 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;11631164 1165 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1166 Ok(())1167 }11681169 fn init_collection(item: &CollectionType<T::AccountId>){11701171 1172 assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");1173 assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");1174 assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");1175 assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");1176 1177 1178 let next_id = CreatedCollectionCount::get()1179 .checked_add(1)1180 .expect("collection id error");1181 1182 CreatedCollectionCount::put(next_id); 1183 }11841185 fn init_nft_token(item: &NftItemType<T::AccountId>){11861187 let current_index = <ItemListIndex>::get(item.collection)1188 .checked_add(1)1189 .expect("Item list index id error");11901191 let item_owner = item.owner.clone();1192 let collection_id = item.collection.clone();1193 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();11941195 <ItemListIndex>::insert(collection_id, current_index);11961197 1198 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1199 .checked_add(1)1200 .unwrap();1201 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1202 }12031204 fn init_fungible_token(item: &FungibleItemType<T::AccountId>){12051206 let current_index = <ItemListIndex>::get(item.collection)1207 .checked_add(1)1208 .expect("Item list index id error");1209 let owner = item.owner.clone();1210 let value = item.value as u64;12111212 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12131214 <ItemListIndex>::insert(item.collection, current_index);12151216 1217 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1218 .checked_add(value)1219 .unwrap();1220 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1221 }12221223 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){12241225 let current_index = <ItemListIndex>::get(item.collection)1226 .checked_add(1)1227 .expect("Item list index id error");12281229 let value = item.owner.first().unwrap().fraction as u64;1230 let owner = item.owner.first().unwrap().owner.clone();12311232 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12331234 <ItemListIndex>::insert(item.collection, current_index);12351236 1237 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1238 .checked_add(value)1239 .unwrap();1240 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1241 }12421243 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {1244 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1245 if list_exists {1246 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1247 let item_contains = list.contains(&item_index.clone());12481249 if !item_contains {1250 list.push(item_index.clone());1251 }12521253 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1254 } else {1255 let mut itm = Vec::new();1256 itm.push(item_index.clone());1257 <AddressTokens<T>>::insert(collection_id, owner, itm);1258 }12591260 Ok(())1261 }12621263 fn remove_token_index(1264 collection_id: u64,1265 item_index: u64,1266 owner: T::AccountId,1267 ) -> DispatchResult {1268 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1269 if list_exists {1270 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1271 let item_contains = list.contains(&item_index.clone());12721273 if item_contains {1274 list.retain(|&item| item != item_index);1275 <AddressTokens<T>>::insert(collection_id, owner, list);1276 }1277 }12781279 Ok(())1280 }12811282 fn move_token_index(1283 collection_id: u64,1284 item_index: u64,1285 old_owner: T::AccountId,1286 new_owner: T::AccountId,1287 ) -> DispatchResult {1288 Self::remove_token_index(collection_id, item_index, old_owner)?;1289 Self::add_token_index(collection_id, item_index, new_owner)?;12901291 Ok(())1292 }1293}1294129512961297129812991300pub type Multiplier = FixedU128;13011302type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1303 <T as system::Trait>::AccountId,1304>>::Balance;1305type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1306 <T as system::Trait>::AccountId,1307>>::NegativeImbalance;1308130913101311#[derive(Encode, Decode, Clone, Eq, PartialEq)]1312pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1313 #[codec(compact)] BalanceOf<T>,1314);13151316impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1317 for ChargeTransactionPayment<T>1318{1319 #[cfg(feature = "std")]1320 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1321 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1322 }1323 #[cfg(not(feature = "std"))]1324 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1325 Ok(())1326 }1327}13281329impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1330where1331 T::Call:1332 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1333 BalanceOf<T>: Send + Sync + FixedPointOperand,1334{1335 1336 pub fn from(fee: BalanceOf<T>) -> Self {1337 Self(fee)1338 }13391340 pub fn traditional_fee(1341 len: usize,1342 info: &DispatchInfoOf<T::Call>,1343 tip: BalanceOf<T>,1344 ) -> BalanceOf<T>1345 where1346 T::Call: Dispatchable<Info = DispatchInfo>,1347 {1348 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1349 }13501351 fn withdraw_fee(1352 &self,1353 who: &T::AccountId,1354 call: &T::Call,1355 info: &DispatchInfoOf<T::Call>,1356 len: usize,1357 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1358 let tip = self.0;13591360 1361 1362 let fee = match call.is_sub_type() {1363 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1364 _ => Self::traditional_fee(len, info, tip), 1365 1366 };13671368 1369 1370 let sponsor: T::AccountId = match call.is_sub_type() {1371 Some(Call::create_item(collection_id, _properties, _owner)) => {1372 <Collection<T>>::get(collection_id).sponsor1373 }1374 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1375 <Collection<T>>::get(collection_id).sponsor1376 }13771378 _ => T::AccountId::default(),1379 };13801381 let mut who_pays_fee: T::AccountId = sponsor.clone();1382 if sponsor == T::AccountId::default() {1383 who_pays_fee = who.clone();1384 }13851386 1387 if fee.is_zero() {1388 return Ok((fee, None));1389 }13901391 match <T as transaction_payment::Trait>::Currency::withdraw(1392 &who_pays_fee,1393 fee,1394 if tip.is_zero() {1395 WithdrawReason::TransactionPayment.into()1396 } else {1397 WithdrawReason::TransactionPayment | WithdrawReason::Tip1398 },1399 ExistenceRequirement::KeepAlive,1400 ) {1401 Ok(imbalance) => Ok((fee, Some(imbalance))),1402 Err(_) => Err(InvalidTransaction::Payment.into()),1403 }1404 }1405}14061407impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1408 for ChargeTransactionPayment<T>1409where1410 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1411 T::Call:1412 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1413{1414 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1415 type AccountId = T::AccountId;1416 type Call = T::Call;1417 type AdditionalSigned = ();1418 type Pre = (1419 BalanceOf<T>,1420 Self::AccountId,1421 Option<NegativeImbalanceOf<T>>,1422 BalanceOf<T>,1423 );1424 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1425 Ok(())1426 }14271428 fn validate(1429 &self,1430 who: &Self::AccountId,1431 call: &Self::Call,1432 info: &DispatchInfoOf<Self::Call>,1433 len: usize,1434 ) -> TransactionValidity {1435 let (fee, _) = self.withdraw_fee(who, call, info, len)?;14361437 let mut r = ValidTransaction::default();1438 1439 1440 r.priority = fee.saturated_into::<TransactionPriority>();1441 Ok(r)1442 }14431444 fn pre_dispatch(1445 self,1446 who: &Self::AccountId,1447 call: &Self::Call,1448 info: &DispatchInfoOf<Self::Call>,1449 len: usize,1450 ) -> Result<Self::Pre, TransactionValidityError> {1451 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1452 Ok((self.0, who.clone(), imbalance, fee))1453 }14541455 fn post_dispatch(1456 pre: Self::Pre,1457 info: &DispatchInfoOf<Self::Call>,1458 post_info: &PostDispatchInfoOf<Self::Call>,1459 len: usize,1460 _result: &DispatchResult,1461 ) -> Result<(), TransactionValidityError> {1462 let (tip, who, imbalance, fee) = pre;1463 if let Some(payed) = imbalance {1464 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1465 len as u32, info, post_info, tip,1466 );1467 let refund = fee.saturating_sub(actual_fee);1468 let actual_payment =1469 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1470 &who, refund,1471 ) {1472 Ok(refund_imbalance) => {1473 1474 1475 match payed.offset(refund_imbalance) {1476 Ok(actual_payment) => actual_payment,1477 Err(_) => return Err(InvalidTransaction::Payment.into()),1478 }1479 }1480 1481 1482 Err(_) => payed,1483 };1484 let imbalances = actual_payment.split(tip);1485 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1486 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1487 );1488 }1489 Ok(())1490 }1491}1492