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, 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, SaturatedConversion, Saturating,31 SignedExtension, Zero,32 },33 transaction_validity::{34 InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,35 ValidTransaction,36 },37 FixedPointOperand, FixedU128,38};3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546474849#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]50#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]51pub enum CollectionMode {52 Invalid,53 54 NFT(u32),55 56 Fungible(u32),57 58 ReFungible(u32, u32),59}6061impl Into<u8> for CollectionMode {62 fn into(self) -> u8 {63 match self {64 CollectionMode::Invalid => 0,65 CollectionMode::NFT(_) => 1,66 CollectionMode::Fungible(_) => 2,67 CollectionMode::ReFungible(_, _) => 3,68 }69 }70}7172#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]73#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]74pub enum AccessMode {75 Normal,76 WhiteList,77}78impl Default for AccessMode {79 fn default() -> Self {80 Self::Normal81 }82}8384impl Default for CollectionMode {85 fn default() -> Self {86 Self::Invalid87 }88}8990#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]91#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]92pub struct Ownership<AccountId> {93 pub owner: AccountId,94 pub fraction: u128,95}9697#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]98#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]99pub struct CollectionType<AccountId> {100 pub owner: AccountId,101 pub mode: CollectionMode,102 pub access: AccessMode,103 pub decimal_points: u32,104 pub name: Vec<u16>, 105 pub description: Vec<u16>, 106 pub token_prefix: Vec<u8>, 107 pub custom_data_size: u32,108 pub mint_mode: bool,109 pub offchain_schema: Vec<u8>,110 pub sponsor: AccountId, 111 pub unconfirmed_sponsor: AccountId, 112}113114#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]115#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]116pub struct CollectionAdminsType<AccountId> {117 pub admin: AccountId,118 pub collection_id: u64,119}120121#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]122#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]123pub struct NftItemType<AccountId> {124 pub collection: u64,125 pub owner: AccountId,126 pub data: Vec<u8>,127}128129#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]130#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]131pub struct FungibleItemType<AccountId> {132 pub collection: u64,133 pub owner: AccountId,134 pub value: u128,135}136137#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]138#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]139pub struct ReFungibleItemType<AccountId> {140 pub collection: u64,141 pub owner: Vec<Ownership<AccountId>>,142 pub data: Vec<u8>,143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct ApprovePermissions<AccountId> {148 pub approved: AccountId,149 pub amount: u64,150}151152#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154pub struct VestingItem<AccountId, Moment> {155 pub sender: AccountId,156 pub recipient: AccountId,157 pub collection_id: u64,158 pub item_id: u64,159 pub amount: u64,160 pub vesting_date: Moment,161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct BasketItem<AccountId, BlockNumber> {166 pub address: AccountId,167 pub start_block: BlockNumber,168}169170#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct ChainLimits {173 pub collection_numbers_limit: u64,174 pub account_token_ownership_limit: u64,175 pub collections_admins_limit: u64,176 pub custom_data_limit: u32,177178 179 pub nft_sponsor_transfer_timeout: u32,180 pub fungible_sponsor_transfer_timeout: u32,181 pub refungible_sponsor_transfer_timeout: u32,182}183184pub trait Trait: system::Trait {185 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;186}187188189#[cfg(feature = "runtime-benchmarks")]190mod benchmarking {191 use super::*;192 use sp_std::prelude::*;193 use frame_system::RawOrigin;194 195 use frame_benchmarking::{benchmarks, account}; 196 use crate::Module as Nft;197198 const SEED: u32 = 1;199200 benchmarks! {201202 _ {}203204 create_collection {205 let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();206 let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();207 let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();208 let mode: CollectionMode = CollectionMode::NFT(2000);209 let caller: T::AccountId = account("caller", 0, SEED);210 }: create_collection(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)211 verify {212 assert_eq!(Nft::<T>::collection(2).owner, caller);213 }214 }215216 #[cfg(test)]217 mod tests {218 use super::*;219 use crate::tests_composite::{ExtBuilder, Test};220 use frame_support::assert_ok;221222 #[test]223 fn create_collection() {224 ExtBuilder::default().build().execute_with(|| {225 assert_ok!(test_benchmark_create_collection::<Test>());226 });227 }228 }229}230231232233decl_storage! {234 trait Store for Module<T: Trait> as Nft {235236 237 NextCollectionID: u64;238 CreatedCollectionCount: u64;239 ChainVersion: u64;240 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;241242 243 pub ChainLimit get(fn chain_limit) config(): ChainLimits;244245 246 CollectionCount: u64;247 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;248249 250 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;251 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;252 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;253254 255 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;256257 258 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;259260 261 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;262 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;263 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;264265 266 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;267268 269 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;270 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>>;271 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;272273 274 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;275 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;276 }277 add_extra_genesis {278 build(|config: &GenesisConfig<T>| {279 280 for (_num, _c) in &config.collection {281 <Module<T>>::init_collection(_c);282 }283284 for (_num, _q, _i) in &config.nft_item_id {285 <Module<T>>::init_nft_token(_i);286 }287288 for (_num, _q, _i) in &config.fungible_item_id {289 <Module<T>>::init_fungible_token(_i);290 }291292 for (_num, _q, _i) in &config.refungible_item_id {293 <Module<T>>::init_refungible_token(_i);294 }295 })296 }297}298299decl_event!(300 pub enum Event<T>301 where302 AccountId = <T as system::Trait>::AccountId,303 {304 305 306 307 308 309 310 311 312 313 Created(u64, u8, AccountId),314315 316 317 318 319 320 321 322 ItemCreated(u64, u64),323324 325 326 327 328 329 330 331 ItemDestroyed(u64, u64),332 }333);334335decl_module! {336 pub struct Module<T: Trait> for enum Call where origin: T::Origin {337338 fn deposit_event() = default;339340 fn on_initialize(now: T::BlockNumber) -> Weight {341342 if ChainVersion::get() < 2343 {344 let value = NextCollectionID::get();345 CreatedCollectionCount::put(value);346 ChainVersion::put(2);347 }348349 0350 }351352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 #[weight = 0]369 pub fn create_collection(origin,370 collection_name: Vec<u16>,371 collection_description: Vec<u16>,372 token_prefix: Vec<u8>,373 mode: CollectionMode) -> DispatchResult {374375 376 let who = ensure_signed(origin)?;377 let custom_data_size = match mode {378 CollectionMode::NFT(size) => {379380 381 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");382 size383 },384 CollectionMode::ReFungible(size, _) => {385386 387 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");388 size389 },390 _ => 0391 };392393 let decimal_points = match mode {394 CollectionMode::Fungible(points) => points,395 CollectionMode::ReFungible(_, points) => points,396 _ => 0397 };398399 400 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");401402 403 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");404405 let mut name = collection_name.to_vec();406 name.push(0);407 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");408409 let mut description = collection_description.to_vec();410 description.push(0);411 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");412413 let mut prefix = token_prefix.to_vec();414 prefix.push(0);415 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");416417 418 let next_id = CreatedCollectionCount::get()419 .checked_add(1)420 .expect("collection id error");421422 423 let total = CollectionCount::get()424 .checked_add(1)425 .expect("collection counter error");426427 CreatedCollectionCount::put(next_id);428 CollectionCount::put(total);429430 431 let new_collection = CollectionType {432 owner: who.clone(),433 name: name,434 mode: mode.clone(),435 mint_mode: false,436 access: AccessMode::Normal,437 description: description,438 decimal_points: decimal_points,439 token_prefix: prefix,440 offchain_schema: Vec::new(),441 custom_data_size: custom_data_size,442 sponsor: T::AccountId::default(),443 unconfirmed_sponsor: T::AccountId::default(),444 };445446 447 <Collection<T>>::insert(next_id, new_collection);448449 450 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));451452 Ok(())453 }454455 456 457 458 459 460 461 462 463 464 #[weight = 0]465 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {466467 let sender = ensure_signed(origin)?;468 Self::check_owner_permissions(collection_id, sender)?;469470 <AddressTokens<T>>::remove_prefix(collection_id);471 <ApprovedList<T>>::remove_prefix(collection_id);472 <Balance<T>>::remove_prefix(collection_id);473 <ItemListIndex>::remove(collection_id);474 <AdminList<T>>::remove(collection_id);475 <Collection<T>>::remove(collection_id);476 <WhiteList<T>>::remove(collection_id);477478 <NftItemList<T>>::remove_prefix(collection_id);479 <FungibleItemList<T>>::remove_prefix(collection_id);480 <ReFungibleItemList<T>>::remove_prefix(collection_id);481482 <NftTransferBasket<T>>::remove_prefix(collection_id);483 <FungibleTransferBasket<T>>::remove_prefix(collection_id);484 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);485486 if CollectionCount::get() > 0487 {488 489 let total = CollectionCount::get()490 .checked_sub(1)491 .expect("collection counter error");492493 CollectionCount::put(total);494 }495496 Ok(())497 }498499 500 501 502 503 504 505 506 507 508 509 510 511 #[weight = 0]512 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{513514 let sender = ensure_signed(origin)?;515 Self::check_owner_or_admin_permissions(collection_id, sender)?;516517 let mut white_list_collection: Vec<T::AccountId>;518 if <WhiteList<T>>::contains_key(collection_id) {519 white_list_collection = <WhiteList<T>>::get(collection_id);520 if !white_list_collection.contains(&address.clone())521 {522 white_list_collection.push(address.clone());523 }524 }525 else {526 white_list_collection = Vec::new();527 white_list_collection.push(address.clone());528 }529530 <WhiteList<T>>::insert(collection_id, white_list_collection);531 Ok(())532 }533534 535 536 537 538 539 540 541 542 543 544 545 546 #[weight = 0]547 pub fn remove_from_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 if <WhiteList<T>>::contains_key(collection_id) {553 let mut white_list_collection = <WhiteList<T>>::get(collection_id);554 if white_list_collection.contains(&address.clone())555 {556 white_list_collection.retain(|i| *i != address.clone());557 <WhiteList<T>>::insert(collection_id, white_list_collection);558 }559 }560561 Ok(())562 }563564 565 566 567 568 569 570 571 572 573 574 575 #[weight = 0]576 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult577 {578 let sender = ensure_signed(origin)?;579580 Self::check_owner_permissions(collection_id, sender)?;581 let mut target_collection = <Collection<T>>::get(collection_id);582 target_collection.access = mode;583 <Collection<T>>::insert(collection_id, target_collection);584585 Ok(())586 }587588 589 590 591 592 593 594 595 596 597 598 599 600 601 #[weight = 0]602 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult603 {604 let sender = ensure_signed(origin)?;605606 Self::check_owner_permissions(collection_id, sender)?;607 let mut target_collection = <Collection<T>>::get(collection_id);608 target_collection.mint_mode = mint_permission;609 <Collection<T>>::insert(collection_id, target_collection);610611 Ok(())612 }613614 615 616 617 618 619 620 621 622 623 624 625 #[weight = 0]626 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {627628 let sender = ensure_signed(origin)?;629 Self::check_owner_permissions(collection_id, sender)?;630 let mut target_collection = <Collection<T>>::get(collection_id);631 target_collection.owner = new_owner;632 <Collection<T>>::insert(collection_id, target_collection);633634 Ok(())635 }636637 638 639 640 641 642 643 644 645 646 647 648 649 650 #[weight = 0]651 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {652653 let sender = ensure_signed(origin)?;654 Self::check_owner_or_admin_permissions(collection_id, sender)?;655 let mut admin_arr: Vec<T::AccountId> = Vec::new();656657 if <AdminList<T>>::contains_key(collection_id)658 {659 admin_arr = <AdminList<T>>::get(collection_id);660 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");661 }662663 664 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");665666 admin_arr.push(new_admin_id);667 <AdminList<T>>::insert(collection_id, admin_arr);668669 Ok(())670 }671672 673 674 675 676 677 678 679 680 681 682 683 684 #[weight = 0]685 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {686687 let sender = ensure_signed(origin)?;688 Self::check_owner_or_admin_permissions(collection_id, sender)?;689690 if <AdminList<T>>::contains_key(collection_id)691 {692 let mut admin_arr = <AdminList<T>>::get(collection_id);693 admin_arr.retain(|i| *i != account_id);694 <AdminList<T>>::insert(collection_id, admin_arr);695 }696697 Ok(())698 }699700 701 702 703 704 705 706 707 708 709 #[weight = 0]710 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {711712 let sender = ensure_signed(origin)?;713 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");714715 let mut target_collection = <Collection<T>>::get(collection_id);716 ensure!(sender == target_collection.owner, "You do not own this collection");717718 target_collection.unconfirmed_sponsor = new_sponsor;719 <Collection<T>>::insert(collection_id, target_collection);720721 Ok(())722 }723724 725 726 727 728 729 730 731 #[weight = 0]732 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {733734 let sender = ensure_signed(origin)?;735 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");736737 let mut target_collection = <Collection<T>>::get(collection_id);738 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");739740 target_collection.sponsor = target_collection.unconfirmed_sponsor;741 target_collection.unconfirmed_sponsor = T::AccountId::default();742 <Collection<T>>::insert(collection_id, target_collection);743744 Ok(())745 }746747 748 749 750 751 752 753 754 755 756 #[weight = 0]757 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {758759 let sender = ensure_signed(origin)?;760 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");761762 let mut target_collection = <Collection<T>>::get(collection_id);763 ensure!(sender == target_collection.owner, "You do not own this collection");764765 target_collection.sponsor = T::AccountId::default();766 <Collection<T>>::insert(collection_id, target_collection);767768 Ok(())769 }770771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 #[weight = 0]790 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {791792 let sender = ensure_signed(origin)?;793 Self::collection_exists(collection_id)?;794 let target_collection = <Collection<T>>::get(collection_id);795796 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {797 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");798 Self::check_white_list(collection_id, &owner)?;799 Self::check_white_list(collection_id, &sender)?;800 }801802 match target_collection.mode803 {804 CollectionMode::NFT(_) => {805806 807 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");808809 810 let item = NftItemType {811 collection: collection_id,812 owner: owner,813 data: properties.clone(),814 };815816 Self::add_nft_item(item)?;817818 },819 CollectionMode::Fungible(_) => {820821 822 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");823824 let item = FungibleItemType {825 collection: collection_id,826 owner: owner,827 value: (10 as u128).pow(target_collection.decimal_points)828 };829830 Self::add_fungible_item(item)?;831 },832 CollectionMode::ReFungible(_, _) => {833834 835 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");836837 let mut owner_list = Vec::new();838 let value = (10 as u128).pow(target_collection.decimal_points);839 owner_list.push(Ownership {owner: owner.clone(), fraction: value});840841 let item = ReFungibleItemType {842 collection: collection_id,843 owner: owner_list,844 data: properties.clone()845 };846847 Self::add_refungible_item(item)?;848 },849 _ => { ensure!(1 == 0,"just error"); }850851 };852853 854 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));855856 Ok(())857 }858859 860 861 862 863 864 865 866 867 868 869 870 871 872 #[weight = 0]873 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {874875 let sender = ensure_signed(origin)?;876 Self::collection_exists(collection_id)?;877878 879 let target_collection = <Collection<T>>::get(collection_id);880 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||881 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),882 "Only item owner, collection owner and admins can modify item");883884 if target_collection.access == AccessMode::WhiteList {885 Self::check_white_list(collection_id, &sender)?;886 }887888 match target_collection.mode889 {890 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,891 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,892 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,893 _ => ()894 };895896 897 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));898899 Ok(())900 }901902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 #[weight = 0]926 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {927928 let sender = ensure_signed(origin)?;929930 931 let target_collection = <Collection<T>>::get(collection_id);932 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||933 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),934 "Only item owner, collection owner and admins can modify item");935936 if target_collection.access == AccessMode::WhiteList {937 Self::check_white_list(collection_id, &sender)?;938 Self::check_white_list(collection_id, &recipient)?;939 }940941 match target_collection.mode942 {943 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,944 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,945 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,946 _ => ()947 };948949 Ok(())950 }951952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 #[weight = 0]968 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {969970 let sender = ensure_signed(origin)?;971972 973 let target_collection = <Collection<T>>::get(collection_id);974 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||975 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),976 "Only item owner, collection owner and admins can approve");977978 if target_collection.access == AccessMode::WhiteList {979 Self::check_white_list(collection_id, &sender)?;980 Self::check_white_list(collection_id, &approved)?;981 }982983 984 let amount = 100000000;985986 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));987 if list_exists {988989 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));990 let item_contains = list.iter().any(|i| i.approved == approved);991992 if !item_contains {993 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });994 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);995 }996 } else {997998 let mut list = Vec::new();999 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1000 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1001 }10021003 Ok(())1004 }1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 #[weight = 0]1026 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10271028 let sender = ensure_signed(origin)?;1029 let mut appoved_transfer = false;10301031 1032 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1033 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1034 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1035 appoved_transfer = opt_item.is_some();1036 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1037 }10381039 1040 let target_collection = <Collection<T>>::get(collection_id);1041 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1042 "Only item owner, collection owner and admins can modify items");10431044 if target_collection.access == AccessMode::WhiteList {1045 Self::check_white_list(collection_id, &sender)?;1046 Self::check_white_list(collection_id, &recipient)?;1047 }10481049 1050 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1051 .into_iter().filter(|i| i.approved != sender.clone()).collect();1052 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);105310541055 match target_collection.mode1056 {1057 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1058 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1059 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1060 _ => ()1061 };10621063 Ok(())1064 }10651066 1067 #[weight = 0]1068 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10691070 1071 1072 1073 10741075 10761077 10781079 Ok(())1080 }10811082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 #[weight = 0]1095 pub fn set_offchain_schema(1096 origin,1097 collection_id: u64,1098 schema: Vec<u8>1099 ) -> DispatchResult {1100 let sender = ensure_signed(origin)?;1101 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11021103 let mut target_collection = <Collection<T>>::get(collection_id);1104 target_collection.offchain_schema = schema;1105 <Collection<T>>::insert(collection_id, target_collection);11061107 Ok(())1108 }11091110 1111 #[weight = 0]1112 pub fn set_chain_limits(1113 origin,1114 limits: ChainLimits1115 ) -> DispatchResult {1116 ensure_root(origin)?;1117 <ChainLimit>::put(limits);1118 Ok(())1119 } 1120 }1121}11221123impl<T: Trait> Module<T> {1124 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1125 let current_index = <ItemListIndex>::get(item.collection)1126 .checked_add(1)1127 .expect("Item list index id error");1128 let itemcopy = item.clone();1129 let owner = item.owner.clone();1130 let value = item.value as u64;11311132 Self::add_token_index(item.collection, current_index, owner.clone())?;11331134 <ItemListIndex>::insert(item.collection, current_index);1135 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11361137 1138 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1139 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1140 1141 1142 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1143 .checked_add(value)1144 .unwrap();1145 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11461147 Ok(())1148 }11491150 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1151 let current_index = <ItemListIndex>::get(item.collection)1152 .checked_add(1)1153 .expect("Item list index id error");1154 let itemcopy = item.clone();11551156 let value = item.owner.first().unwrap().fraction as u64;1157 let owner = item.owner.first().unwrap().owner.clone();11581159 Self::add_token_index(item.collection, current_index, owner.clone())?;11601161 <ItemListIndex>::insert(item.collection, current_index);1162 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11631164 1165 let block_number: T::BlockNumber = 0.into();1166 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11671168 1169 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1170 .checked_add(value)1171 .unwrap();1172 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11731174 Ok(())1175 }11761177 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1178 let current_index = <ItemListIndex>::get(item.collection)1179 .checked_add(1)1180 .expect("Item list index id error");11811182 let item_owner = item.owner.clone();1183 let collection_id = item.collection.clone();1184 Self::add_token_index(collection_id, current_index, item.owner.clone())?;11851186 <ItemListIndex>::insert(collection_id, current_index);1187 <NftItemList<T>>::insert(collection_id, current_index, item);11881189 1190 let block_number: T::BlockNumber = 0.into();1191 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);11921193 1194 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1195 .checked_add(1)1196 .unwrap();1197 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);11981199 Ok(())1200 }12011202 fn burn_refungible_item(1203 collection_id: u64,1204 item_id: u64,1205 owner: T::AccountId,1206 ) -> DispatchResult {1207 ensure!(1208 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1209 "Item does not exists"1210 );1211 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1212 let item = collection1213 .owner1214 .iter()1215 .filter(|&i| i.owner == owner)1216 .next()1217 .unwrap();1218 Self::remove_token_index(collection_id, item_id, owner.clone())?;12191220 1221 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12221223 1224 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1225 .checked_sub(item.fraction as u64)1226 .unwrap();1227 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12281229 <ReFungibleItemList<T>>::remove(collection_id, item_id);12301231 Ok(())1232 }12331234 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1235 ensure!(1236 <NftItemList<T>>::contains_key(collection_id, item_id),1237 "Item does not exists"1238 );1239 let item = <NftItemList<T>>::get(collection_id, item_id);1240 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12411242 1243 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12441245 1246 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1247 .checked_sub(1)1248 .unwrap();1249 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1250 <NftItemList<T>>::remove(collection_id, item_id);12511252 Ok(())1253 }12541255 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1256 ensure!(1257 <FungibleItemList<T>>::contains_key(collection_id, item_id),1258 "Item does not exists"1259 );1260 let item = <FungibleItemList<T>>::get(collection_id, item_id);1261 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12621263 1264 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12651266 1267 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1268 .checked_sub(item.value as u64)1269 .unwrap();1270 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12711272 <FungibleItemList<T>>::remove(collection_id, item_id);12731274 Ok(())1275 }12761277 fn collection_exists(collection_id: u64) -> DispatchResult {1278 ensure!(1279 <Collection<T>>::contains_key(collection_id),1280 "This collection does not exist"1281 );1282 Ok(())1283 }12841285 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1286 Self::collection_exists(collection_id)?;12871288 let target_collection = <Collection<T>>::get(collection_id);1289 ensure!(1290 subject == target_collection.owner,1291 "You do not own this collection"1292 );12931294 Ok(())1295 }12961297 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1298 let target_collection = <Collection<T>>::get(collection_id);1299 let mut result: bool = subject == target_collection.owner;1300 let exists = <AdminList<T>>::contains_key(collection_id);13011302 if !result & exists {1303 if <AdminList<T>>::get(collection_id).contains(&subject) {1304 result = true1305 }1306 }13071308 result1309 }13101311 fn check_owner_or_admin_permissions(1312 collection_id: u64,1313 subject: T::AccountId,1314 ) -> DispatchResult {1315 Self::collection_exists(collection_id)?;1316 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13171318 ensure!(1319 result,1320 "You do not have permissions to modify this collection"1321 );1322 Ok(())1323 }13241325 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1326 let target_collection = <Collection<T>>::get(collection_id);13271328 match target_collection.mode {1329 CollectionMode::NFT(_) => {1330 <NftItemList<T>>::get(collection_id, item_id).owner == subject1331 }1332 CollectionMode::Fungible(_) => {1333 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1334 }1335 CollectionMode::ReFungible(_, _) => {1336 <ReFungibleItemList<T>>::get(collection_id, item_id)1337 .owner1338 .iter()1339 .any(|i| i.owner == subject)1340 }1341 CollectionMode::Invalid => false,1342 }1343 }13441345 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1346 let mes = "Address is not in white list";1347 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1348 let wl = <WhiteList<T>>::get(collection_id);1349 ensure!(wl.contains(address), mes);13501351 Ok(())1352 }13531354 fn transfer_fungible(1355 collection_id: u64,1356 item_id: u64,1357 value: u64,1358 owner: T::AccountId,1359 new_owner: T::AccountId,1360 ) -> DispatchResult {1361 ensure!(1362 <FungibleItemList<T>>::contains_key(collection_id, item_id),1363 "Item not exists"1364 );13651366 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1367 let amount = full_item.value;13681369 ensure!(amount >= value.into(), "Item balance not enouth");13701371 1372 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1373 .checked_sub(value)1374 .unwrap();1375 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);13761377 let mut new_owner_account_id = 0;1378 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1379 if new_owner_items.len() > 0 {1380 new_owner_account_id = new_owner_items[0];1381 }13821383 let val64 = value.into();13841385 1386 if amount == val64 && new_owner_account_id == 0 {1387 1388 1389 let mut new_full_item = full_item.clone();1390 new_full_item.owner = new_owner.clone();1391 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);13921393 1394 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1395 .checked_add(value)1396 .unwrap();1397 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);13981399 1400 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1401 } else {1402 let mut new_full_item = full_item.clone();1403 new_full_item.value -= val64;14041405 1406 if new_owner_account_id > 0 {1407 1408 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1409 item.value += val64;14101411 1412 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1413 .checked_add(value)1414 .unwrap();1415 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14161417 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1418 } else {1419 1420 let item = FungibleItemType {1421 collection: collection_id,1422 owner: new_owner.clone(),1423 value: val64,1424 };14251426 Self::add_fungible_item(item)?;1427 }14281429 if amount == val64 {1430 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14311432 1433 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1434 <FungibleItemList<T>>::remove(collection_id, item_id);1435 }14361437 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1438 }14391440 Ok(())1441 }14421443 fn transfer_refungible(1444 collection_id: u64,1445 item_id: u64,1446 value: u64,1447 owner: T::AccountId,1448 new_owner: T::AccountId,1449 ) -> DispatchResult {1450 ensure!(1451 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1452 "Item not exists"1453 );14541455 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1456 let item = full_item1457 .owner1458 .iter()1459 .filter(|i| i.owner == owner)1460 .next()1461 .unwrap();1462 let amount = item.fraction;14631464 ensure!(amount >= value.into(), "Item balance not enouth");14651466 1467 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1468 .checked_sub(value)1469 .unwrap();1470 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14711472 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1473 .checked_add(value)1474 .unwrap();1475 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14761477 let old_owner = item.owner.clone();1478 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1479 let val64 = value.into();14801481 1482 if amount == val64 && !new_owner_has_account {1483 1484 1485 let mut new_full_item = full_item.clone();1486 new_full_item1487 .owner1488 .iter_mut()1489 .find(|i| i.owner == owner)1490 .unwrap()1491 .owner = new_owner.clone();1492 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14931494 1495 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1496 } else {1497 let mut new_full_item = full_item.clone();1498 new_full_item1499 .owner1500 .iter_mut()1501 .find(|i| i.owner == owner)1502 .unwrap()1503 .fraction -= val64;15041505 1506 if new_owner_has_account {1507 1508 new_full_item1509 .owner1510 .iter_mut()1511 .find(|i| i.owner == new_owner)1512 .unwrap()1513 .fraction += val64;1514 } else {1515 1516 new_full_item.owner.push(Ownership {1517 owner: new_owner.clone(),1518 fraction: val64,1519 });1520 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1521 }15221523 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1524 }15251526 Ok(())1527 }15281529 fn transfer_nft(1530 collection_id: u64,1531 item_id: u64,1532 sender: T::AccountId,1533 new_owner: T::AccountId,1534 ) -> DispatchResult {1535 ensure!(1536 <NftItemList<T>>::contains_key(collection_id, item_id),1537 "Item not exists"1538 );15391540 let mut item = <NftItemList<T>>::get(collection_id, item_id);15411542 ensure!(1543 sender == item.owner,1544 "sender parameter and item owner must be equal"1545 );15461547 1548 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1549 .checked_sub(1)1550 .unwrap();1551 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15521553 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1554 .checked_add(1)1555 .unwrap();1556 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15571558 1559 let old_owner = item.owner.clone();1560 item.owner = new_owner.clone();1561 <NftItemList<T>>::insert(collection_id, item_id, item);15621563 1564 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15651566 1567 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1568 Ok(())1569 }15701571 fn init_collection(item: &CollectionType<T::AccountId>) {1572 1573 assert!(1574 item.decimal_points <= 4,1575 "decimal_points parameter must be lower than 4"1576 );1577 assert!(1578 item.name.len() <= 64,1579 "Collection name can not be longer than 63 char"1580 );1581 assert!(1582 item.name.len() <= 256,1583 "Collection description can not be longer than 255 char"1584 );1585 assert!(1586 item.token_prefix.len() <= 16,1587 "Token prefix can not be longer than 15 char"1588 );15891590 1591 let next_id = CreatedCollectionCount::get()1592 .checked_add(1)1593 .expect("collection id error");15941595 CreatedCollectionCount::put(next_id);1596 }15971598 fn init_nft_token(item: &NftItemType<T::AccountId>) {1599 let current_index = <ItemListIndex>::get(item.collection)1600 .checked_add(1)1601 .expect("Item list index id error");16021603 let item_owner = item.owner.clone();1604 let collection_id = item.collection.clone();1605 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16061607 <ItemListIndex>::insert(collection_id, current_index);16081609 1610 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1611 .checked_add(1)1612 .unwrap();1613 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1614 }16151616 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1617 let current_index = <ItemListIndex>::get(item.collection)1618 .checked_add(1)1619 .expect("Item list index id error");1620 let owner = item.owner.clone();1621 let value = item.value as u64;16221623 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16241625 <ItemListIndex>::insert(item.collection, current_index);16261627 1628 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1629 .checked_add(value)1630 .unwrap();1631 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1632 }16331634 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1635 let current_index = <ItemListIndex>::get(item.collection)1636 .checked_add(1)1637 .expect("Item list index id error");16381639 let value = item.owner.first().unwrap().fraction as u64;1640 let owner = item.owner.first().unwrap().owner.clone();16411642 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16431644 <ItemListIndex>::insert(item.collection, current_index);16451646 1647 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1648 .checked_add(value)1649 .unwrap();1650 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1651 }16521653 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16541655 1656 if <AccountItemCount<T>>::contains_key(owner.clone()) {16571658 1659 let count = <AccountItemCount<T>>::get(owner.clone());1660 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16611662 <AccountItemCount<T>>::insert(owner.clone(), 1663 count.checked_add(1).unwrap());1664 }1665 else {1666 <AccountItemCount<T>>::insert(owner.clone(), 1);1667 }16681669 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1670 if list_exists {1671 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1672 let item_contains = list.contains(&item_index.clone());16731674 if !item_contains {1675 list.push(item_index.clone());1676 }16771678 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1679 } else {1680 let mut itm = Vec::new();1681 itm.push(item_index.clone());1682 <AddressTokens<T>>::insert(collection_id, owner, itm);1683 1684 }16851686 Ok(())1687 }16881689 fn remove_token_index(1690 collection_id: u64,1691 item_index: u64,1692 owner: T::AccountId,1693 ) -> DispatchResult {16941695 1696 <AccountItemCount<T>>::insert(owner.clone(), 1697 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());169816991700 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1701 if list_exists {1702 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1703 let item_contains = list.contains(&item_index.clone());17041705 if item_contains {1706 list.retain(|&item| item != item_index);1707 <AddressTokens<T>>::insert(collection_id, owner, list);1708 }1709 }17101711 Ok(())1712 }17131714 fn move_token_index(1715 collection_id: u64,1716 item_index: u64,1717 old_owner: T::AccountId,1718 new_owner: T::AccountId,1719 ) -> DispatchResult {1720 Self::remove_token_index(collection_id, item_index, old_owner)?;1721 Self::add_token_index(collection_id, item_index, new_owner)?;17221723 Ok(())1724 }1725}1726172717281729173017311732pub type Multiplier = FixedU128;17331734type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1735 <T as system::Trait>::AccountId,1736>>::Balance;1737type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1738 <T as system::Trait>::AccountId,1739>>::NegativeImbalance;1740174117421743#[derive(Encode, Decode, Clone, Eq, PartialEq)]1744pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1745 #[codec(compact)] BalanceOf<T>,1746);17471748impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1749 for ChargeTransactionPayment<T>1750{1751 #[cfg(feature = "std")]1752 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1753 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1754 }1755 #[cfg(not(feature = "std"))]1756 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1757 Ok(())1758 }1759}17601761impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1762where1763 T::Call:1764 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1765 BalanceOf<T>: Send + Sync + FixedPointOperand,1766{1767 1768 pub fn from(fee: BalanceOf<T>) -> Self {1769 Self(fee)1770 }17711772 pub fn traditional_fee(1773 len: usize,1774 info: &DispatchInfoOf<T::Call>,1775 tip: BalanceOf<T>,1776 ) -> BalanceOf<T>1777 where1778 T::Call: Dispatchable<Info = DispatchInfo>,1779 {1780 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1781 }17821783 fn withdraw_fee(1784 &self,1785 who: &T::AccountId,1786 call: &T::Call,1787 info: &DispatchInfoOf<T::Call>,1788 len: usize,1789 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1790 let tip = self.0;17911792 1793 1794 let fee = match call.is_sub_type() {1795 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1796 _ => Self::traditional_fee(len, info, tip), 1797 1798 };17991800 1801 1802 let sponsor: T::AccountId = match call.is_sub_type() {1803 Some(Call::create_item(collection_id, _properties, _owner)) => {1804 <Collection<T>>::get(collection_id).sponsor1805 }1806 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1807 let _collection_mode = <Collection<T>>::get(collection_id).mode;18081809 1810 let sponsor_transfer = match _collection_mode {1811 CollectionMode::NFT(_) => {1812 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1813 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1814 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1815 if block_number >= limit_time {1816 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1817 true1818 }1819 else {1820 false1821 }1822 }1823 CollectionMode::Fungible(_) => {1824 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1825 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1826 if basket.iter().any(|i| i.address == _new_owner.clone())1827 {1828 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1829 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1830 if block_number >= limit_time {1831 basket.retain(|x| x.address == item.address);1832 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1833 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1834 true1835 }1836 else {1837 false1838 }1839 }1840 else {1841 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1842 true1843 }1844 }1845 CollectionMode::ReFungible(_, _) => {1846 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1847 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1848 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1849 if block_number >= limit_time {1850 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1851 true1852 } else {1853 false1854 }1855 }1856 _ => {1857 false1858 },1859 };18601861 if !sponsor_transfer {1862 T::AccountId::default()1863 } else {1864 <Collection<T>>::get(collection_id).sponsor1865 }1866 }18671868 _ => T::AccountId::default(),1869 };18701871 let mut who_pays_fee: T::AccountId = sponsor.clone();1872 if sponsor == T::AccountId::default() {1873 who_pays_fee = who.clone();1874 }18751876 1877 if fee.is_zero() {1878 return Ok((fee, None));1879 }18801881 match <T as transaction_payment::Trait>::Currency::withdraw(1882 &who_pays_fee,1883 fee,1884 if tip.is_zero() {1885 WithdrawReason::TransactionPayment.into()1886 } else {1887 WithdrawReason::TransactionPayment | WithdrawReason::Tip1888 },1889 ExistenceRequirement::KeepAlive,1890 ) {1891 Ok(imbalance) => Ok((fee, Some(imbalance))),1892 Err(_) => Err(InvalidTransaction::Payment.into()),1893 }1894 }1895}18961897impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1898 for ChargeTransactionPayment<T>1899where1900 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1901 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1902{1903 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1904 type AccountId = T::AccountId;1905 type Call = T::Call;1906 type AdditionalSigned = ();1907 type Pre = (1908 BalanceOf<T>,1909 Self::AccountId,1910 Option<NegativeImbalanceOf<T>>,1911 BalanceOf<T>,1912 );1913 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1914 Ok(())1915 }19161917 fn validate(1918 &self,1919 who: &Self::AccountId,1920 call: &Self::Call,1921 info: &DispatchInfoOf<Self::Call>,1922 len: usize,1923 ) -> TransactionValidity {1924 let (fee, _) = self.withdraw_fee(who, call, info, len)?;19251926 let mut r = ValidTransaction::default();1927 1928 1929 r.priority = fee.saturated_into::<TransactionPriority>();1930 Ok(r)1931 }19321933 fn pre_dispatch(1934 self,1935 who: &Self::AccountId,1936 call: &Self::Call,1937 info: &DispatchInfoOf<Self::Call>,1938 len: usize,1939 ) -> Result<Self::Pre, TransactionValidityError> {1940 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1941 Ok((self.0, who.clone(), imbalance, fee))1942 }19431944 fn post_dispatch(1945 pre: Self::Pre,1946 info: &DispatchInfoOf<Self::Call>,1947 post_info: &PostDispatchInfoOf<Self::Call>,1948 len: usize,1949 _result: &DispatchResult,1950 ) -> Result<(), TransactionValidityError> {1951 let (tip, who, imbalance, fee) = pre;1952 if let Some(payed) = imbalance {1953 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1954 len as u32, info, post_info, tip,1955 );1956 let refund = fee.saturating_sub(actual_fee);1957 let actual_payment =1958 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1959 &who, refund,1960 ) {1961 Ok(refund_imbalance) => {1962 1963 1964 match payed.offset(refund_imbalance) {1965 Ok(actual_payment) => actual_payment,1966 Err(_) => return Err(InvalidTransaction::Payment.into()),1967 }1968 }1969 1970 1971 Err(_) => payed,1972 };1973 let imbalances = actual_payment.split(tip);1974 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1975 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1976 );1977 }1978 Ok(())1979 }1980}198119821983