1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage,12 dispatch::DispatchResult,13 ensure, fail, parameter_types,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};252627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30 traits::{31 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,32 SignedExtension, Zero,33 },34 transaction_validity::{35 InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,36 ValidTransaction,37 },38 FixedPointOperand, FixedU128,39};4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849505152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55 Invalid,56 57 NFT(u32),58 59 Fungible(u32),60 61 ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65 fn into(self) -> u8 {66 match self {67 CollectionMode::Invalid => 0,68 CollectionMode::NFT(_) => 1,69 CollectionMode::Fungible(_) => 2,70 CollectionMode::ReFungible(_, _) => 3,71 }72 }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78 Normal,79 WhiteList,80}81impl Default for AccessMode {82 fn default() -> Self {83 Self::Normal84 }85}8687impl Default for CollectionMode {88 fn default() -> Self {89 Self::Invalid90 }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96 pub owner: AccountId,97 pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103 pub owner: AccountId,104 pub mode: CollectionMode,105 pub access: AccessMode,106 pub decimal_points: u32,107 pub name: Vec<u16>, 108 pub description: Vec<u16>, 109 pub token_prefix: Vec<u8>, 110 pub custom_data_size: u32,111 pub mint_mode: bool,112 pub offchain_schema: Vec<u8>,113 pub sponsor: AccountId, 114 pub unconfirmed_sponsor: AccountId, 115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120 pub admin: AccountId,121 pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127 pub collection: u64,128 pub owner: AccountId,129 pub data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135 pub collection: u64,136 pub owner: AccountId,137 pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143 pub collection: u64,144 pub owner: Vec<Ownership<AccountId>>,145 pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151 pub approved: AccountId,152 pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158 pub sender: AccountId,159 pub recipient: AccountId,160 pub collection_id: u64,161 pub item_id: u64,162 pub amount: u64,163 pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169 pub address: AccountId,170 pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176 pub collection_numbers_limit: u64,177 pub account_token_ownership_limit: u64,178 pub collections_admins_limit: u64,179 pub custom_data_limit: u32,180181 182 pub nft_sponsor_transfer_timeout: u32,183 pub fungible_sponsor_transfer_timeout: u32,184 pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188 fn create_collection() -> Weight;189 fn destroy_collection() -> Weight;190 fn add_to_white_list() -> Weight;191 fn remove_from_white_list() -> Weight;192 fn set_public_access_mode() -> Weight;193 fn set_mint_permission() -> Weight;194 fn change_collection_owner() -> Weight;195 fn add_collection_admin() -> Weight;196 fn remove_collection_admin() -> Weight;197 fn set_collection_sponsor() -> Weight;198 fn confirm_sponsorship() -> Weight;199 fn remove_collection_sponsor() -> Weight;200 fn create_item(s: usize, ) -> Weight;201 fn burn_item() -> Weight;202 fn transfer() -> Weight;203 fn approve() -> Weight;204 fn transfer_from() -> Weight;205 fn set_offchain_schema() -> Weight;206}207208pub trait Trait: system::Trait + Sized {209 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;210211 212 type WeightInfo: WeightInfo;213}214215#[cfg(feature = "runtime-benchmarks")]216mod benchmarking;217218219220decl_storage! {221 trait Store for Module<T: Trait> as Nft {222223 224 NextCollectionID: u64;225 CreatedCollectionCount: u64;226 ChainVersion: u64;227 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;228229 230 pub ChainLimit get(fn chain_limit) config(): ChainLimits;231232 233 CollectionCount: u64;234 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;235236 237 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;238 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;239 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;240241 242 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;243244 245 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;246247 248 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;249 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;250 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;251252 253 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;254255 256 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;257 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>>;258 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;259260 261 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;262 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;263 }264 add_extra_genesis {265 build(|config: &GenesisConfig<T>| {266 267 for (_num, _c) in &config.collection {268 <Module<T>>::init_collection(_c);269 }270271 for (_num, _q, _i) in &config.nft_item_id {272 <Module<T>>::init_nft_token(_i);273 }274275 for (_num, _q, _i) in &config.fungible_item_id {276 <Module<T>>::init_fungible_token(_i);277 }278279 for (_num, _q, _i) in &config.refungible_item_id {280 <Module<T>>::init_refungible_token(_i);281 }282 })283 }284}285286decl_event!(287 pub enum Event<T>288 where289 AccountId = <T as system::Trait>::AccountId,290 {291 292 293 294 295 296 297 298 299 300 Created(u64, u8, AccountId),301302 303 304 305 306 307 308 309 ItemCreated(u64, u64),310311 312 313 314 315 316 317 318 ItemDestroyed(u64, u64),319 }320);321322decl_module! {323 pub struct Module<T: Trait> for enum Call where origin: T::Origin {324325 fn deposit_event() = default;326327 fn on_initialize(now: T::BlockNumber) -> Weight {328329 if ChainVersion::get() < 2330 {331 let value = NextCollectionID::get();332 CreatedCollectionCount::put(value);333 ChainVersion::put(2);334 }335336 0337 }338339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 #[weight = T::WeightInfo::create_collection()]356 pub fn create_collection(origin,357 collection_name: Vec<u16>,358 collection_description: Vec<u16>,359 token_prefix: Vec<u8>,360 mode: CollectionMode) -> DispatchResult {361362 363 let who = ensure_signed(origin)?;364 let custom_data_size = match mode {365 CollectionMode::NFT(size) => {366367 368 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");369 size370 },371 CollectionMode::ReFungible(size, _) => {372373 374 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");375 size376 },377 _ => 0378 };379380 let decimal_points = match mode {381 CollectionMode::Fungible(points) => points,382 CollectionMode::ReFungible(_, points) => points,383 _ => 0384 };385386 387 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");388389 390 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");391392 let mut name = collection_name.to_vec();393 name.push(0);394 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");395396 let mut description = collection_description.to_vec();397 description.push(0);398 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");399400 let mut prefix = token_prefix.to_vec();401 prefix.push(0);402 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");403404 405 let next_id = CreatedCollectionCount::get()406 .checked_add(1)407 .expect("collection id error");408409 410 let total = CollectionCount::get()411 .checked_add(1)412 .expect("collection counter error");413414 CreatedCollectionCount::put(next_id);415 CollectionCount::put(total);416417 418 let new_collection = CollectionType {419 owner: who.clone(),420 name: name,421 mode: mode.clone(),422 mint_mode: false,423 access: AccessMode::Normal,424 description: description,425 decimal_points: decimal_points,426 token_prefix: prefix,427 offchain_schema: Vec::new(),428 custom_data_size: custom_data_size,429 sponsor: T::AccountId::default(),430 unconfirmed_sponsor: T::AccountId::default(),431 };432433 434 <Collection<T>>::insert(next_id, new_collection);435436 437 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));438439 Ok(())440 }441442 443 444 445 446 447 448 449 450 451 #[weight = T::WeightInfo::destroy_collection()]452 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {453454 let sender = ensure_signed(origin)?;455 Self::check_owner_permissions(collection_id, sender)?;456457 <AddressTokens<T>>::remove_prefix(collection_id);458 <ApprovedList<T>>::remove_prefix(collection_id);459 <Balance<T>>::remove_prefix(collection_id);460 <ItemListIndex>::remove(collection_id);461 <AdminList<T>>::remove(collection_id);462 <Collection<T>>::remove(collection_id);463 <WhiteList<T>>::remove(collection_id);464465 <NftItemList<T>>::remove_prefix(collection_id);466 <FungibleItemList<T>>::remove_prefix(collection_id);467 <ReFungibleItemList<T>>::remove_prefix(collection_id);468469 <NftTransferBasket<T>>::remove_prefix(collection_id);470 <FungibleTransferBasket<T>>::remove_prefix(collection_id);471 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);472473 if CollectionCount::get() > 0474 {475 476 let total = CollectionCount::get()477 .checked_sub(1)478 .expect("collection counter error");479480 CollectionCount::put(total);481 }482483 Ok(())484 }485486 487 488 489 490 491 492 493 494 495 496 497 498 #[weight = T::WeightInfo::add_to_white_list()]499 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{500501 let sender = ensure_signed(origin)?;502 Self::check_owner_or_admin_permissions(collection_id, sender)?;503504 let mut white_list_collection: Vec<T::AccountId>;505 if <WhiteList<T>>::contains_key(collection_id) {506 white_list_collection = <WhiteList<T>>::get(collection_id);507 if !white_list_collection.contains(&address.clone())508 {509 white_list_collection.push(address.clone());510 }511 }512 else {513 white_list_collection = Vec::new();514 white_list_collection.push(address.clone());515 }516517 <WhiteList<T>>::insert(collection_id, white_list_collection);518 Ok(())519 }520521 522 523 524 525 526 527 528 529 530 531 532 533 #[weight = T::WeightInfo::remove_from_white_list()]534 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{535536 let sender = ensure_signed(origin)?;537 Self::check_owner_or_admin_permissions(collection_id, sender)?;538539 if <WhiteList<T>>::contains_key(collection_id) {540 let mut white_list_collection = <WhiteList<T>>::get(collection_id);541 if white_list_collection.contains(&address.clone())542 {543 white_list_collection.retain(|i| *i != address.clone());544 <WhiteList<T>>::insert(collection_id, white_list_collection);545 }546 }547548 Ok(())549 }550551 552 553 554 555 556 557 558 559 560 561 562 #[weight = T::WeightInfo::set_public_access_mode()]563 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult564 {565 let sender = ensure_signed(origin)?;566567 Self::check_owner_permissions(collection_id, sender)?;568 let mut target_collection = <Collection<T>>::get(collection_id);569 target_collection.access = mode;570 <Collection<T>>::insert(collection_id, target_collection);571572 Ok(())573 }574575 576 577 578 579 580 581 582 583 584 585 586 587 588 #[weight = T::WeightInfo::set_mint_permission()]589 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult590 {591 let sender = ensure_signed(origin)?;592593 Self::check_owner_permissions(collection_id, sender)?;594 let mut target_collection = <Collection<T>>::get(collection_id);595 target_collection.mint_mode = mint_permission;596 <Collection<T>>::insert(collection_id, target_collection);597598 Ok(())599 }600601 602 603 604 605 606 607 608 609 610 611 612 #[weight = T::WeightInfo::change_collection_owner()]613 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {614615 let sender = ensure_signed(origin)?;616 Self::check_owner_permissions(collection_id, sender)?;617 let mut target_collection = <Collection<T>>::get(collection_id);618 target_collection.owner = new_owner;619 <Collection<T>>::insert(collection_id, target_collection);620621 Ok(())622 }623624 625 626 627 628 629 630 631 632 633 634 635 636 637 #[weight = T::WeightInfo::add_collection_admin()]638 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {639640 let sender = ensure_signed(origin)?;641 Self::check_owner_or_admin_permissions(collection_id, sender)?;642 let mut admin_arr: Vec<T::AccountId> = Vec::new();643644 if <AdminList<T>>::contains_key(collection_id)645 {646 admin_arr = <AdminList<T>>::get(collection_id);647 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");648 }649650 651 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");652653 admin_arr.push(new_admin_id);654 <AdminList<T>>::insert(collection_id, admin_arr);655656 Ok(())657 }658659 660 661 662 663 664 665 666 667 668 669 670 671 #[weight = T::WeightInfo::remove_collection_admin()]672 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {673674 let sender = ensure_signed(origin)?;675 Self::check_owner_or_admin_permissions(collection_id, sender)?;676677 if <AdminList<T>>::contains_key(collection_id)678 {679 let mut admin_arr = <AdminList<T>>::get(collection_id);680 admin_arr.retain(|i| *i != account_id);681 <AdminList<T>>::insert(collection_id, admin_arr);682 }683684 Ok(())685 }686687 688 689 690 691 692 693 694 695 696 #[weight = T::WeightInfo::set_collection_sponsor()]697 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {698699 let sender = ensure_signed(origin)?;700 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");701702 let mut target_collection = <Collection<T>>::get(collection_id);703 ensure!(sender == target_collection.owner, "You do not own this collection");704705 target_collection.unconfirmed_sponsor = new_sponsor;706 <Collection<T>>::insert(collection_id, target_collection);707708 Ok(())709 }710711 712 713 714 715 716 717 718 #[weight = T::WeightInfo::confirm_sponsorship()]719 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {720721 let sender = ensure_signed(origin)?;722 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");723724 let mut target_collection = <Collection<T>>::get(collection_id);725 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");726727 target_collection.sponsor = target_collection.unconfirmed_sponsor;728 target_collection.unconfirmed_sponsor = T::AccountId::default();729 <Collection<T>>::insert(collection_id, target_collection);730731 Ok(())732 }733734 735 736 737 738 739 740 741 742 743 #[weight = T::WeightInfo::remove_collection_sponsor()]744 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {745746 let sender = ensure_signed(origin)?;747 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");748749 let mut target_collection = <Collection<T>>::get(collection_id);750 ensure!(sender == target_collection.owner, "You do not own this collection");751752 target_collection.sponsor = T::AccountId::default();753 <Collection<T>>::insert(collection_id, target_collection);754755 Ok(())756 }757758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781782 #[weight = T::WeightInfo::create_item(properties.len())]783 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {784785 let sender = ensure_signed(origin)?;786787 Self::collection_exists(collection_id)?;788789 let target_collection = <Collection<T>>::get(collection_id);790791 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;792 Self::validate_create_item_args(&target_collection, &properties)?;793 Self::create_item_no_validation(collection_id, &target_collection, &properties, &owner)?;794795 Ok(())796 }797798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 #[weight = 0]817 pub fn create_multiple_items(origin, collection_id: u64, properties: Vec<Vec<u8>>, owner: T::AccountId) -> DispatchResult {818819 ensure!(properties.len() > 0, "Length of items properties must be greater than 0.");820 let sender = ensure_signed(origin)?;821822 Self::collection_exists(collection_id)?;823 let target_collection = <Collection<T>>::get(collection_id);824825 Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;826827 for prop in &properties {828 Self::validate_create_item_args(&target_collection, prop)?;829 }830 for prop in &properties {831 Self::create_item_no_validation(collection_id, &target_collection, prop, &owner)?;832 }833834 Ok(())835 }836837 838 839 840 841 842 843 844 845 846 847 848 849 850 #[weight = T::WeightInfo::burn_item()]851 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {852853 let sender = ensure_signed(origin)?;854 Self::collection_exists(collection_id)?;855856 857 let target_collection = <Collection<T>>::get(collection_id);858 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||859 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),860 "Only item owner, collection owner and admins can modify item");861862 if target_collection.access == AccessMode::WhiteList {863 Self::check_white_list(collection_id, &sender)?;864 }865866 match target_collection.mode867 {868 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,869 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,870 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,871 _ => ()872 };873874 875 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));876877 Ok(())878 }879880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 #[weight = T::WeightInfo::transfer()]904 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {905906 let sender = ensure_signed(origin)?;907908 909 let target_collection = <Collection<T>>::get(collection_id);910 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||911 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),912 "Only item owner, collection owner and admins can modify item");913914 if target_collection.access == AccessMode::WhiteList {915 Self::check_white_list(collection_id, &sender)?;916 Self::check_white_list(collection_id, &recipient)?;917 }918919 match target_collection.mode920 {921 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,922 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,923 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,924 _ => ()925 };926927 Ok(())928 }929930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 #[weight = T::WeightInfo::approve()]946 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {947948 let sender = ensure_signed(origin)?;949950 951 let target_collection = <Collection<T>>::get(collection_id);952 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||953 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),954 "Only item owner, collection owner and admins can approve");955956 if target_collection.access == AccessMode::WhiteList {957 Self::check_white_list(collection_id, &sender)?;958 Self::check_white_list(collection_id, &approved)?;959 }960961 962 let amount = 100000000;963964 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));965 if list_exists {966967 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));968 let item_contains = list.iter().any(|i| i.approved == approved);969970 if !item_contains {971 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });972 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);973 }974 } else {975976 let mut list = Vec::new();977 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });978 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);979 }980981 Ok(())982 }983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 #[weight = T::WeightInfo::transfer_from()]1004 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10051006 let sender = ensure_signed(origin)?;1007 let mut appoved_transfer = false;10081009 1010 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1011 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1012 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1013 if opt_item.is_some()1014 {1015 appoved_transfer = true;1016 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1017 }1018 }10191020 1021 let target_collection = <Collection<T>>::get(collection_id);1022 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023 "Only item owner, collection owner and admins can modify items");10241025 if target_collection.access == AccessMode::WhiteList {1026 Self::check_white_list(collection_id, &sender)?;1027 Self::check_white_list(collection_id, &recipient)?;1028 }10291030 1031 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1032 .into_iter().filter(|i| i.approved != sender.clone()).collect();1033 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);103410351036 match target_collection.mode1037 {1038 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1039 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1040 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1041 _ => ()1042 };10431044 Ok(())1045 }10461047 1048 #[weight = 0]1049 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10501051 1052 1053 1054 10551056 10571058 10591060 Ok(())1061 }10621063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 #[weight = T::WeightInfo::set_offchain_schema()]1076 pub fn set_offchain_schema(1077 origin,1078 collection_id: u64,1079 schema: Vec<u8>1080 ) -> DispatchResult {1081 let sender = ensure_signed(origin)?;1082 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10831084 let mut target_collection = <Collection<T>>::get(collection_id);1085 target_collection.offchain_schema = schema;1086 <Collection<T>>::insert(collection_id, target_collection);10871088 Ok(())1089 }10901091 1092 #[weight = 0]1093 pub fn set_chain_limits(1094 origin,1095 limits: ChainLimits1096 ) -> DispatchResult {1097 ensure_root(origin)?;1098 <ChainLimit>::put(limits);1099 Ok(())1100 } 1101 }1102}11031104impl<T: Trait> Module<T> {11051106 fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {11071108 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1109 ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1110 Self::check_white_list(collection_id, owner)?;1111 Self::check_white_list(collection_id, sender)?;1112 }11131114 Ok(())1115 }11161117 fn validate_create_item_args(collection: &CollectionType<T::AccountId>, properties: &Vec<u8>) -> DispatchResult {11181119 match collection.mode1120 {1121 CollectionMode::NFT(_) => {11221123 1124 ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1125 },1126 CollectionMode::Fungible(_) => {11271128 1129 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type")1130 },1131 CollectionMode::ReFungible(_, _) => {11321133 1134 ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")1135 },1136 _ => {1137 fail!("Unexpected collection mode")1138 }1139 }11401141 Ok(())1142 }11431144 fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, properties: &Vec<u8>, owner: &T::AccountId) -> DispatchResult {1145 match collection.mode1146 {1147 CollectionMode::NFT(_) => {11481149 1150 let item = NftItemType {1151 collection: collection_id,1152 owner: owner.clone(),1153 data: properties.clone(),1154 };11551156 Self::add_nft_item(item)?;11571158 },1159 CollectionMode::Fungible(_) => {11601161 let item = FungibleItemType {1162 collection: collection_id,1163 owner: owner.clone(),1164 value: (10 as u128).pow(collection.decimal_points)1165 };11661167 Self::add_fungible_item(item)?;1168 },1169 CollectionMode::ReFungible(_, _) => {11701171 let mut owner_list = Vec::new();1172 let value = (10 as u128).pow(collection.decimal_points);1173 owner_list.push(Ownership {owner: owner.clone(), fraction: value});11741175 let item = ReFungibleItemType {1176 collection: collection_id,1177 owner: owner_list,1178 data: properties.clone()1179 };11801181 Self::add_refungible_item(item)?;1182 },1183 _ => { ensure!(1 == 0,"just error"); }11841185 };11861187 1188 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));11891190 Ok(())1191 }11921193 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1194 let current_index = <ItemListIndex>::get(item.collection)1195 .checked_add(1)1196 .expect("Item list index id error");1197 let itemcopy = item.clone();1198 let owner = item.owner.clone();1199 let value = item.value as u64;12001201 Self::add_token_index(item.collection, current_index, owner.clone())?;12021203 <ItemListIndex>::insert(item.collection, current_index);1204 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12051206 1207 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1208 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1209 1210 1211 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1212 .checked_add(value)1213 .unwrap();1214 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12151216 Ok(())1217 }12181219 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1220 let current_index = <ItemListIndex>::get(item.collection)1221 .checked_add(1)1222 .expect("Item list index id error");1223 let itemcopy = item.clone();12241225 let value = item.owner.first().unwrap().fraction as u64;1226 let owner = item.owner.first().unwrap().owner.clone();12271228 Self::add_token_index(item.collection, current_index, owner.clone())?;12291230 <ItemListIndex>::insert(item.collection, current_index);1231 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12321233 1234 let block_number: T::BlockNumber = 0.into();1235 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);12361237 1238 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1239 .checked_add(value)1240 .unwrap();1241 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12421243 Ok(())1244 }12451246 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1247 let current_index = <ItemListIndex>::get(item.collection)1248 .checked_add(1)1249 .expect("Item list index id error");12501251 let item_owner = item.owner.clone();1252 let collection_id = item.collection.clone();1253 Self::add_token_index(collection_id, current_index, item.owner.clone())?;12541255 <ItemListIndex>::insert(collection_id, current_index);1256 <NftItemList<T>>::insert(collection_id, current_index, item);12571258 1259 let block_number: T::BlockNumber = 0.into();1260 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12611262 1263 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1264 .checked_add(1)1265 .unwrap();1266 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12671268 Ok(())1269 }12701271 fn burn_refungible_item(1272 collection_id: u64,1273 item_id: u64,1274 owner: T::AccountId,1275 ) -> DispatchResult {1276 ensure!(1277 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1278 "Item does not exists"1279 );1280 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1281 let item = collection1282 .owner1283 .iter()1284 .filter(|&i| i.owner == owner)1285 .next()1286 .unwrap();1287 Self::remove_token_index(collection_id, item_id, owner.clone())?;12881289 1290 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12911292 1293 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1294 .checked_sub(item.fraction as u64)1295 .unwrap();1296 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12971298 <ReFungibleItemList<T>>::remove(collection_id, item_id);12991300 Ok(())1301 }13021303 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1304 ensure!(1305 <NftItemList<T>>::contains_key(collection_id, item_id),1306 "Item does not exists"1307 );1308 let item = <NftItemList<T>>::get(collection_id, item_id);1309 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13101311 1312 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13131314 1315 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1316 .checked_sub(1)1317 .unwrap();1318 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1319 <NftItemList<T>>::remove(collection_id, item_id);13201321 Ok(())1322 }13231324 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1325 ensure!(1326 <FungibleItemList<T>>::contains_key(collection_id, item_id),1327 "Item does not exists"1328 );1329 let item = <FungibleItemList<T>>::get(collection_id, item_id);1330 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13311332 1333 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13341335 1336 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1337 .checked_sub(item.value as u64)1338 .unwrap();1339 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13401341 <FungibleItemList<T>>::remove(collection_id, item_id);13421343 Ok(())1344 }13451346 fn collection_exists(collection_id: u64) -> DispatchResult {1347 ensure!(1348 <Collection<T>>::contains_key(collection_id),1349 "This collection does not exist"1350 );1351 Ok(())1352 }13531354 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1355 Self::collection_exists(collection_id)?;13561357 let target_collection = <Collection<T>>::get(collection_id);1358 ensure!(1359 subject == target_collection.owner,1360 "You do not own this collection"1361 );13621363 Ok(())1364 }13651366 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1367 let target_collection = <Collection<T>>::get(collection_id);1368 let mut result: bool = subject == target_collection.owner;1369 let exists = <AdminList<T>>::contains_key(collection_id);13701371 if !result & exists {1372 if <AdminList<T>>::get(collection_id).contains(&subject) {1373 result = true1374 }1375 }13761377 result1378 }13791380 fn check_owner_or_admin_permissions(1381 collection_id: u64,1382 subject: T::AccountId,1383 ) -> DispatchResult {1384 Self::collection_exists(collection_id)?;1385 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13861387 ensure!(1388 result,1389 "You do not have permissions to modify this collection"1390 );1391 Ok(())1392 }13931394 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1395 let target_collection = <Collection<T>>::get(collection_id);13961397 match target_collection.mode {1398 CollectionMode::NFT(_) => {1399 <NftItemList<T>>::get(collection_id, item_id).owner == subject1400 }1401 CollectionMode::Fungible(_) => {1402 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1403 }1404 CollectionMode::ReFungible(_, _) => {1405 <ReFungibleItemList<T>>::get(collection_id, item_id)1406 .owner1407 .iter()1408 .any(|i| i.owner == subject)1409 }1410 CollectionMode::Invalid => false,1411 }1412 }14131414 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1415 let mes = "Address is not in white list";1416 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1417 let wl = <WhiteList<T>>::get(collection_id);1418 ensure!(wl.contains(address), mes);14191420 Ok(())1421 }14221423 fn transfer_fungible(1424 collection_id: u64,1425 item_id: u64,1426 value: u64,1427 owner: T::AccountId,1428 new_owner: T::AccountId,1429 ) -> DispatchResult {1430 ensure!(1431 <FungibleItemList<T>>::contains_key(collection_id, item_id),1432 "Item not exists"1433 );14341435 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1436 let amount = full_item.value;14371438 ensure!(amount >= value.into(), "Item balance not enouth");14391440 1441 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1442 .checked_sub(value)1443 .unwrap();1444 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);14451446 let mut new_owner_account_id = 0;1447 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1448 if new_owner_items.len() > 0 {1449 new_owner_account_id = new_owner_items[0];1450 }14511452 let val64 = value.into();14531454 1455 if amount == val64 && new_owner_account_id == 0 {1456 1457 1458 let mut new_full_item = full_item.clone();1459 new_full_item.owner = new_owner.clone();1460 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14611462 1463 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1464 .checked_add(value)1465 .unwrap();1466 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14671468 1469 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1470 } else {1471 let mut new_full_item = full_item.clone();1472 new_full_item.value -= val64;14731474 1475 if new_owner_account_id > 0 {1476 1477 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1478 item.value += val64;14791480 1481 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1482 .checked_add(value)1483 .unwrap();1484 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14851486 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1487 } else {1488 1489 let item = FungibleItemType {1490 collection: collection_id,1491 owner: new_owner.clone(),1492 value: val64,1493 };14941495 Self::add_fungible_item(item)?;1496 }14971498 if amount == val64 {1499 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15001501 1502 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1503 <FungibleItemList<T>>::remove(collection_id, item_id);1504 }15051506 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1507 }15081509 Ok(())1510 }15111512 fn transfer_refungible(1513 collection_id: u64,1514 item_id: u64,1515 value: u64,1516 owner: T::AccountId,1517 new_owner: T::AccountId,1518 ) -> DispatchResult {1519 ensure!(1520 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1521 "Item not exists"1522 );15231524 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1525 let item = full_item1526 .owner1527 .iter()1528 .filter(|i| i.owner == owner)1529 .next()1530 .unwrap();1531 let amount = item.fraction;15321533 ensure!(amount >= value.into(), "Item balance not enouth");15341535 1536 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1537 .checked_sub(value)1538 .unwrap();1539 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15401541 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1542 .checked_add(value)1543 .unwrap();1544 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15451546 let old_owner = item.owner.clone();1547 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1548 let val64 = value.into();15491550 1551 if amount == val64 && !new_owner_has_account {1552 1553 1554 let mut new_full_item = full_item.clone();1555 new_full_item1556 .owner1557 .iter_mut()1558 .find(|i| i.owner == owner)1559 .unwrap()1560 .owner = new_owner.clone();1561 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15621563 1564 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1565 } else {1566 let mut new_full_item = full_item.clone();1567 new_full_item1568 .owner1569 .iter_mut()1570 .find(|i| i.owner == owner)1571 .unwrap()1572 .fraction -= val64;15731574 1575 if new_owner_has_account {1576 1577 new_full_item1578 .owner1579 .iter_mut()1580 .find(|i| i.owner == new_owner)1581 .unwrap()1582 .fraction += val64;1583 } else {1584 1585 new_full_item.owner.push(Ownership {1586 owner: new_owner.clone(),1587 fraction: val64,1588 });1589 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1590 }15911592 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1593 }15941595 Ok(())1596 }15971598 fn transfer_nft(1599 collection_id: u64,1600 item_id: u64,1601 sender: T::AccountId,1602 new_owner: T::AccountId,1603 ) -> DispatchResult {1604 ensure!(1605 <NftItemList<T>>::contains_key(collection_id, item_id),1606 "Item not exists"1607 );16081609 let mut item = <NftItemList<T>>::get(collection_id, item_id);16101611 ensure!(1612 sender == item.owner,1613 "sender parameter and item owner must be equal"1614 );16151616 1617 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1618 .checked_sub(1)1619 .unwrap();1620 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16211622 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1623 .checked_add(1)1624 .unwrap();1625 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16261627 1628 let old_owner = item.owner.clone();1629 item.owner = new_owner.clone();1630 <NftItemList<T>>::insert(collection_id, item_id, item);16311632 1633 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;16341635 1636 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1637 Ok(())1638 }16391640 fn init_collection(item: &CollectionType<T::AccountId>) {1641 1642 assert!(1643 item.decimal_points <= 4,1644 "decimal_points parameter must be lower than 4"1645 );1646 assert!(1647 item.name.len() <= 64,1648 "Collection name can not be longer than 63 char"1649 );1650 assert!(1651 item.name.len() <= 256,1652 "Collection description can not be longer than 255 char"1653 );1654 assert!(1655 item.token_prefix.len() <= 16,1656 "Token prefix can not be longer than 15 char"1657 );16581659 1660 let next_id = CreatedCollectionCount::get()1661 .checked_add(1)1662 .expect("collection id error");16631664 CreatedCollectionCount::put(next_id);1665 }16661667 fn init_nft_token(item: &NftItemType<T::AccountId>) {1668 let current_index = <ItemListIndex>::get(item.collection)1669 .checked_add(1)1670 .expect("Item list index id error");16711672 let item_owner = item.owner.clone();1673 let collection_id = item.collection.clone();1674 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16751676 <ItemListIndex>::insert(collection_id, current_index);16771678 1679 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1680 .checked_add(1)1681 .unwrap();1682 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1683 }16841685 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1686 let current_index = <ItemListIndex>::get(item.collection)1687 .checked_add(1)1688 .expect("Item list index id error");1689 let owner = item.owner.clone();1690 let value = item.value as u64;16911692 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16931694 <ItemListIndex>::insert(item.collection, current_index);16951696 1697 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1698 .checked_add(value)1699 .unwrap();1700 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1701 }17021703 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1704 let current_index = <ItemListIndex>::get(item.collection)1705 .checked_add(1)1706 .expect("Item list index id error");17071708 let value = item.owner.first().unwrap().fraction as u64;1709 let owner = item.owner.first().unwrap().owner.clone();17101711 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();17121713 <ItemListIndex>::insert(item.collection, current_index);17141715 1716 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1717 .checked_add(value)1718 .unwrap();1719 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1720 }17211722 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {17231724 1725 if <AccountItemCount<T>>::contains_key(owner.clone()) {17261727 1728 let count = <AccountItemCount<T>>::get(owner.clone());1729 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");17301731 <AccountItemCount<T>>::insert(owner.clone(), 1732 count.checked_add(1).unwrap());1733 }1734 else {1735 <AccountItemCount<T>>::insert(owner.clone(), 1);1736 }17371738 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1739 if list_exists {1740 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1741 let item_contains = list.contains(&item_index.clone());17421743 if !item_contains {1744 list.push(item_index.clone());1745 }17461747 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1748 } else {1749 let mut itm = Vec::new();1750 itm.push(item_index.clone());1751 <AddressTokens<T>>::insert(collection_id, owner, itm);1752 1753 }17541755 Ok(())1756 }17571758 fn remove_token_index(1759 collection_id: u64,1760 item_index: u64,1761 owner: T::AccountId,1762 ) -> DispatchResult {17631764 1765 <AccountItemCount<T>>::insert(owner.clone(), 1766 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());176717681769 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1770 if list_exists {1771 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1772 let item_contains = list.contains(&item_index.clone());17731774 if item_contains {1775 list.retain(|&item| item != item_index);1776 <AddressTokens<T>>::insert(collection_id, owner, list);1777 }1778 }17791780 Ok(())1781 }17821783 fn move_token_index(1784 collection_id: u64,1785 item_index: u64,1786 old_owner: T::AccountId,1787 new_owner: T::AccountId,1788 ) -> DispatchResult {1789 Self::remove_token_index(collection_id, item_index, old_owner)?;1790 Self::add_token_index(collection_id, item_index, new_owner)?;17911792 Ok(())1793 }1794}1795179617971798179918001801pub type Multiplier = FixedU128;18021803type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1804 <T as system::Trait>::AccountId,1805>>::Balance;1806type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1807 <T as system::Trait>::AccountId,1808>>::NegativeImbalance;1809181018111812#[derive(Encode, Decode, Clone, Eq, PartialEq)]1813pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1814 #[codec(compact)] BalanceOf<T>,1815);18161817impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1818 for ChargeTransactionPayment<T>1819{1820 #[cfg(feature = "std")]1821 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1822 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1823 }1824 #[cfg(not(feature = "std"))]1825 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1826 Ok(())1827 }1828}18291830impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1831where1832 T::Call:1833 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1834 BalanceOf<T>: Send + Sync + FixedPointOperand,1835{1836 1837 pub fn from(fee: BalanceOf<T>) -> Self {1838 Self(fee)1839 }18401841 pub fn traditional_fee(1842 len: usize,1843 info: &DispatchInfoOf<T::Call>,1844 tip: BalanceOf<T>,1845 ) -> BalanceOf<T>1846 where1847 T::Call: Dispatchable<Info = DispatchInfo>,1848 {1849 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1850 }18511852 fn withdraw_fee(1853 &self,1854 who: &T::AccountId,1855 call: &T::Call,1856 info: &DispatchInfoOf<T::Call>,1857 len: usize,1858 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1859 let tip = self.0;18601861 1862 1863 let fee = match call.is_sub_type() {1864 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1865 _ => Self::traditional_fee(len, info, tip), 1866 1867 };18681869 1870 1871 let sponsor: T::AccountId = match call.is_sub_type() {1872 Some(Call::create_item(collection_id, _properties, _owner)) => {1873 <Collection<T>>::get(collection_id).sponsor1874 }1875 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1876 let _collection_mode = <Collection<T>>::get(collection_id).mode;18771878 1879 let sponsor_transfer = match _collection_mode {1880 CollectionMode::NFT(_) => {1881 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1882 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1883 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1884 if block_number >= limit_time {1885 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1886 true1887 }1888 else {1889 false1890 }1891 }1892 CollectionMode::Fungible(_) => {1893 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1894 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1895 if basket.iter().any(|i| i.address == _new_owner.clone())1896 {1897 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1898 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1899 if block_number >= limit_time {1900 basket.retain(|x| x.address == item.address);1901 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1902 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1903 true1904 }1905 else {1906 false1907 }1908 }1909 else {1910 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1911 true1912 }1913 }1914 CollectionMode::ReFungible(_, _) => {1915 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1916 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1917 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1918 if block_number >= limit_time {1919 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1920 true1921 } else {1922 false1923 }1924 }1925 _ => {1926 false1927 },1928 };19291930 if !sponsor_transfer {1931 T::AccountId::default()1932 } else {1933 <Collection<T>>::get(collection_id).sponsor1934 }1935 }19361937 _ => T::AccountId::default(),1938 };19391940 let mut who_pays_fee: T::AccountId = sponsor.clone();1941 if sponsor == T::AccountId::default() {1942 who_pays_fee = who.clone();1943 }19441945 1946 if fee.is_zero() {1947 return Ok((fee, None));1948 }19491950 match <T as transaction_payment::Trait>::Currency::withdraw(1951 &who_pays_fee,1952 fee,1953 if tip.is_zero() {1954 WithdrawReason::TransactionPayment.into()1955 } else {1956 WithdrawReason::TransactionPayment | WithdrawReason::Tip1957 },1958 ExistenceRequirement::KeepAlive,1959 ) {1960 Ok(imbalance) => Ok((fee, Some(imbalance))),1961 Err(_) => Err(InvalidTransaction::Payment.into()),1962 }1963 }1964}19651966impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1967 for ChargeTransactionPayment<T>1968where1969 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1970 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1971{1972 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1973 type AccountId = T::AccountId;1974 type Call = T::Call;1975 type AdditionalSigned = ();1976 type Pre = (1977 BalanceOf<T>,1978 Self::AccountId,1979 Option<NegativeImbalanceOf<T>>,1980 BalanceOf<T>,1981 );1982 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1983 Ok(())1984 }19851986 fn validate(1987 &self,1988 who: &Self::AccountId,1989 call: &Self::Call,1990 info: &DispatchInfoOf<Self::Call>,1991 len: usize,1992 ) -> TransactionValidity {1993 let (fee, _) = self.withdraw_fee(who, call, info, len)?;19941995 let mut r = ValidTransaction::default();1996 1997 1998 r.priority = fee.saturated_into::<TransactionPriority>();1999 Ok(r)2000 }20012002 fn pre_dispatch(2003 self,2004 who: &Self::AccountId,2005 call: &Self::Call,2006 info: &DispatchInfoOf<Self::Call>,2007 len: usize,2008 ) -> Result<Self::Pre, TransactionValidityError> {2009 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2010 Ok((self.0, who.clone(), imbalance, fee))2011 }20122013 fn post_dispatch(2014 pre: Self::Pre,2015 info: &DispatchInfoOf<Self::Call>,2016 post_info: &PostDispatchInfoOf<Self::Call>,2017 len: usize,2018 _result: &DispatchResult,2019 ) -> Result<(), TransactionValidityError> {2020 let (tip, who, imbalance, fee) = pre;2021 if let Some(payed) = imbalance {2022 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2023 len as u32, info, post_info, tip,2024 );2025 let refund = fee.saturating_sub(actual_fee);2026 let actual_payment =2027 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2028 &who, refund,2029 ) {2030 Ok(refund_imbalance) => {2031 2032 2033 match payed.offset(refund_imbalance) {2034 Ok(actual_payment) => actual_payment,2035 Err(_) => return Err(InvalidTransaction::Payment.into()),2036 }2037 }2038 2039 2040 Err(_) => payed,2041 };2042 let imbalances = actual_payment.split(tip);2043 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2044 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2045 );2046 }2047 Ok(())2048 }2049}205020512052