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};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;4647484950#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]51#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]52pub enum CollectionMode {53 Invalid,54 55 NFT(u32),56 57 Fungible(u32),58 59 ReFungible(u32, u32),60}6162impl Into<u8> for CollectionMode {63 fn into(self) -> u8 {64 match self {65 CollectionMode::Invalid => 0,66 CollectionMode::NFT(_) => 1,67 CollectionMode::Fungible(_) => 2,68 CollectionMode::ReFungible(_, _) => 3,69 }70 }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76 Normal,77 WhiteList,78}79impl Default for AccessMode {80 fn default() -> Self {81 Self::Normal82 }83}8485impl Default for CollectionMode {86 fn default() -> Self {87 Self::Invalid88 }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94 pub owner: AccountId,95 pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101 pub owner: AccountId,102 pub mode: CollectionMode,103 pub access: AccessMode,104 pub decimal_points: u32,105 pub name: Vec<u16>, 106 pub description: Vec<u16>, 107 pub token_prefix: Vec<u8>, 108 pub custom_data_size: u32,109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, 112 pub unconfirmed_sponsor: AccountId, 113}114115#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]116#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]117pub struct CollectionAdminsType<AccountId> {118 pub admin: AccountId,119 pub collection_id: u64,120}121122#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]123#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]124pub struct NftItemType<AccountId> {125 pub collection: u64,126 pub owner: AccountId,127 pub data: Vec<u8>,128}129130#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]131#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]132pub struct FungibleItemType<AccountId> {133 pub collection: u64,134 pub owner: AccountId,135 pub value: u128,136}137138#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]139#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]140pub struct ReFungibleItemType<AccountId> {141 pub collection: u64,142 pub owner: Vec<Ownership<AccountId>>,143 pub data: Vec<u8>,144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct ApprovePermissions<AccountId> {149 pub approved: AccountId,150 pub amount: u64,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct VestingItem<AccountId, Moment> {156 pub sender: AccountId,157 pub recipient: AccountId,158 pub collection_id: u64,159 pub item_id: u64,160 pub amount: u64,161 pub vesting_date: Moment,162}163164#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]165#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]166pub struct BasketItem<AccountId, BlockNumber> {167 pub address: AccountId,168 pub start_block: BlockNumber,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ChainLimits {174 pub collection_numbers_limit: u64,175 pub account_token_ownership_limit: u64,176 pub collections_admins_limit: u64,177 pub custom_data_limit: u32,178179 180 pub nft_sponsor_transfer_timeout: u32,181 pub fungible_sponsor_transfer_timeout: u32,182 pub refungible_sponsor_transfer_timeout: u32,183}184185pub trait Trait: system::Trait {186 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;187}188189#[cfg(feature = "runtime-benchmarks")]190mod benchmarking;191192193194decl_storage! {195 trait Store for Module<T: Trait> as Nft {196197 198 NextCollectionID: u64;199 CreatedCollectionCount: u64;200 ChainVersion: u64;201 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;202203 204 pub ChainLimit get(fn chain_limit) config(): ChainLimits;205206 207 CollectionCount: u64;208 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;209210 211 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;212 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;213 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;214215 216 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;217218 219 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;220221 222 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;223 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;224 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;225226 227 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;228229 230 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;231 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>>;232 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;233234 235 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;236 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;237 }238 add_extra_genesis {239 build(|config: &GenesisConfig<T>| {240 241 for (_num, _c) in &config.collection {242 <Module<T>>::init_collection(_c);243 }244245 for (_num, _q, _i) in &config.nft_item_id {246 <Module<T>>::init_nft_token(_i);247 }248249 for (_num, _q, _i) in &config.fungible_item_id {250 <Module<T>>::init_fungible_token(_i);251 }252253 for (_num, _q, _i) in &config.refungible_item_id {254 <Module<T>>::init_refungible_token(_i);255 }256 })257 }258}259260decl_event!(261 pub enum Event<T>262 where263 AccountId = <T as system::Trait>::AccountId,264 {265 266 267 268 269 270 271 272 273 274 Created(u64, u8, AccountId),275276 277 278 279 280 281 282 283 ItemCreated(u64, u64),284285 286 287 288 289 290 291 292 ItemDestroyed(u64, u64),293 }294);295296decl_module! {297 pub struct Module<T: Trait> for enum Call where origin: T::Origin {298299 fn deposit_event() = default;300301 fn on_initialize(now: T::BlockNumber) -> Weight {302303 if ChainVersion::get() < 2304 {305 let value = NextCollectionID::get();306 CreatedCollectionCount::put(value);307 ChainVersion::put(2);308 }309310 0311 }312313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 #[weight =330 (70_000_000 as Weight)331 .saturating_add(RocksDbWeight::get().reads(7 as Weight))332 .saturating_add(RocksDbWeight::get().writes(5 as Weight))]333 pub fn create_collection(origin,334 collection_name: Vec<u16>,335 collection_description: Vec<u16>,336 token_prefix: Vec<u8>,337 mode: CollectionMode) -> DispatchResult {338339 340 let who = ensure_signed(origin)?;341 let custom_data_size = match mode {342 CollectionMode::NFT(size) => {343344 345 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");346 size347 },348 CollectionMode::ReFungible(size, _) => {349350 351 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");352 size353 },354 _ => 0355 };356357 let decimal_points = match mode {358 CollectionMode::Fungible(points) => points,359 CollectionMode::ReFungible(_, points) => points,360 _ => 0361 };362363 364 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");365366 367 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");368369 let mut name = collection_name.to_vec();370 name.push(0);371 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");372373 let mut description = collection_description.to_vec();374 description.push(0);375 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");376377 let mut prefix = token_prefix.to_vec();378 prefix.push(0);379 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");380381 382 let next_id = CreatedCollectionCount::get()383 .checked_add(1)384 .expect("collection id error");385386 387 let total = CollectionCount::get()388 .checked_add(1)389 .expect("collection counter error");390391 CreatedCollectionCount::put(next_id);392 CollectionCount::put(total);393394 395 let new_collection = CollectionType {396 owner: who.clone(),397 name: name,398 mode: mode.clone(),399 mint_mode: false,400 access: AccessMode::Normal,401 description: description,402 decimal_points: decimal_points,403 token_prefix: prefix,404 offchain_schema: Vec::new(),405 custom_data_size: custom_data_size,406 sponsor: T::AccountId::default(),407 unconfirmed_sponsor: T::AccountId::default(),408 };409410 411 <Collection<T>>::insert(next_id, new_collection);412413 414 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));415416 Ok(())417 }418419 420 421 422 423 424 425 426 427 428 #[weight =429 (90_000_000 as Weight)430 .saturating_add(RocksDbWeight::get().reads(2 as Weight))431 .saturating_add(RocksDbWeight::get().writes(5 as Weight))]432 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {433434 let sender = ensure_signed(origin)?;435 Self::check_owner_permissions(collection_id, sender)?;436437 <AddressTokens<T>>::remove_prefix(collection_id);438 <ApprovedList<T>>::remove_prefix(collection_id);439 <Balance<T>>::remove_prefix(collection_id);440 <ItemListIndex>::remove(collection_id);441 <AdminList<T>>::remove(collection_id);442 <Collection<T>>::remove(collection_id);443 <WhiteList<T>>::remove(collection_id);444445 <NftItemList<T>>::remove_prefix(collection_id);446 <FungibleItemList<T>>::remove_prefix(collection_id);447 <ReFungibleItemList<T>>::remove_prefix(collection_id);448449 <NftTransferBasket<T>>::remove_prefix(collection_id);450 <FungibleTransferBasket<T>>::remove_prefix(collection_id);451 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);452453 if CollectionCount::get() > 0454 {455 456 let total = CollectionCount::get()457 .checked_sub(1)458 .expect("collection counter error");459460 CollectionCount::put(total);461 }462463 Ok(())464 }465466 467 468 469 470 471 472 473 474 475 476 477 478 #[weight =479 (30_000_000 as Weight)480 .saturating_add(RocksDbWeight::get().reads(3 as Weight))481 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]482 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{483484 let sender = ensure_signed(origin)?;485 Self::check_owner_or_admin_permissions(collection_id, sender)?;486487 let mut white_list_collection: Vec<T::AccountId>;488 if <WhiteList<T>>::contains_key(collection_id) {489 white_list_collection = <WhiteList<T>>::get(collection_id);490 if !white_list_collection.contains(&address.clone())491 {492 white_list_collection.push(address.clone());493 }494 }495 else {496 white_list_collection = Vec::new();497 white_list_collection.push(address.clone());498 }499500 <WhiteList<T>>::insert(collection_id, white_list_collection);501 Ok(())502 }503504 505 506 507 508 509 510 511 512 513 514 515 516 #[weight =517 (35_000_000 as Weight)518 .saturating_add(RocksDbWeight::get().reads(3 as Weight))519 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]520 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{521522 let sender = ensure_signed(origin)?;523 Self::check_owner_or_admin_permissions(collection_id, sender)?;524525 if <WhiteList<T>>::contains_key(collection_id) {526 let mut white_list_collection = <WhiteList<T>>::get(collection_id);527 if white_list_collection.contains(&address.clone())528 {529 white_list_collection.retain(|i| *i != address.clone());530 <WhiteList<T>>::insert(collection_id, white_list_collection);531 }532 }533534 Ok(())535 }536537 538 539 540 541 542 543 544 545 546 547 548 #[weight =549 (27_000_000 as Weight)550 .saturating_add(RocksDbWeight::get().reads(1 as Weight))551 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]552 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult553 {554 let sender = ensure_signed(origin)?;555556 Self::check_owner_permissions(collection_id, sender)?;557 let mut target_collection = <Collection<T>>::get(collection_id);558 target_collection.access = mode;559 <Collection<T>>::insert(collection_id, target_collection);560561 Ok(())562 }563564 565 566 567 568 569 570 571 572 573 574 575 576 577 #[weight =578 (27_000_000 as Weight)579 .saturating_add(RocksDbWeight::get().reads(1 as Weight))580 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]581 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult582 {583 let sender = ensure_signed(origin)?;584585 Self::check_owner_permissions(collection_id, sender)?;586 let mut target_collection = <Collection<T>>::get(collection_id);587 target_collection.mint_mode = mint_permission;588 <Collection<T>>::insert(collection_id, target_collection);589590 Ok(())591 }592593 594 595 596 597 598 599 600 601 602 603 604 #[weight =605 (27_000_000 as Weight)606 .saturating_add(RocksDbWeight::get().reads(1 as Weight))607 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]608 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {609610 let sender = ensure_signed(origin)?;611 Self::check_owner_permissions(collection_id, sender)?;612 let mut target_collection = <Collection<T>>::get(collection_id);613 target_collection.owner = new_owner;614 <Collection<T>>::insert(collection_id, target_collection);615616 Ok(())617 }618619 620 621 622 623 624 625 626 627 628 629 630 631 632 #[weight =633 (32_000_000 as Weight)634 .saturating_add(RocksDbWeight::get().reads(3 as Weight))635 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]636 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {637638 let sender = ensure_signed(origin)?;639 Self::check_owner_or_admin_permissions(collection_id, sender)?;640 let mut admin_arr: Vec<T::AccountId> = Vec::new();641642 if <AdminList<T>>::contains_key(collection_id)643 {644 admin_arr = <AdminList<T>>::get(collection_id);645 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");646 }647648 649 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");650651 admin_arr.push(new_admin_id);652 <AdminList<T>>::insert(collection_id, admin_arr);653654 Ok(())655 }656657 658 659 660 661 662 663 664 665 666 667 668 669 #[weight =670 (50_000_000 as Weight)671 .saturating_add(RocksDbWeight::get().reads(2 as Weight))672 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]673 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {674675 let sender = ensure_signed(origin)?;676 Self::check_owner_or_admin_permissions(collection_id, sender)?;677678 if <AdminList<T>>::contains_key(collection_id)679 {680 let mut admin_arr = <AdminList<T>>::get(collection_id);681 admin_arr.retain(|i| *i != account_id);682 <AdminList<T>>::insert(collection_id, admin_arr);683 }684685 Ok(())686 }687688 689 690 691 692 693 694 695 696 697 #[weight =698 (32_000_000 as Weight)699 .saturating_add(RocksDbWeight::get().reads(2 as Weight))700 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]701 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {702703 let sender = ensure_signed(origin)?;704 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");705706 let mut target_collection = <Collection<T>>::get(collection_id);707 ensure!(sender == target_collection.owner, "You do not own this collection");708709 target_collection.unconfirmed_sponsor = new_sponsor;710 <Collection<T>>::insert(collection_id, target_collection);711712 Ok(())713 }714715 716 717 718 719 720 721 722 #[weight =723 (22_000_000 as Weight)724 .saturating_add(RocksDbWeight::get().reads(1 as Weight))725 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]726 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {727728 let sender = ensure_signed(origin)?;729 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");730731 let mut target_collection = <Collection<T>>::get(collection_id);732 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");733734 target_collection.sponsor = target_collection.unconfirmed_sponsor;735 target_collection.unconfirmed_sponsor = T::AccountId::default();736 <Collection<T>>::insert(collection_id, target_collection);737738 Ok(())739 }740741 742 743 744 745 746 747 748 749 750 #[weight =751 (24_000_000 as Weight)752 .saturating_add(RocksDbWeight::get().reads(1 as Weight))753 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]754 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {755756 let sender = ensure_signed(origin)?;757 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");758759 let mut target_collection = <Collection<T>>::get(collection_id);760 ensure!(sender == target_collection.owner, "You do not own this collection");761762 target_collection.sponsor = T::AccountId::default();763 <Collection<T>>::insert(collection_id, target_collection);764765 Ok(())766 }767768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 #[weight =787 (130_000_000 as Weight)788 .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))789 .saturating_add(RocksDbWeight::get().reads(10 as Weight))790 .saturating_add(RocksDbWeight::get().writes(8 as Weight))]791 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {792793 let sender = ensure_signed(origin)?;794 Self::collection_exists(collection_id)?;795 let target_collection = <Collection<T>>::get(collection_id);796797 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {798 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");799 Self::check_white_list(collection_id, &owner)?;800 Self::check_white_list(collection_id, &sender)?;801 }802803 match target_collection.mode804 {805 CollectionMode::NFT(_) => {806807 808 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");809810 811 let item = NftItemType {812 collection: collection_id,813 owner: owner,814 data: properties.clone(),815 };816817 Self::add_nft_item(item)?;818819 },820 CollectionMode::Fungible(_) => {821822 823 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");824825 let item = FungibleItemType {826 collection: collection_id,827 owner: owner,828 value: (10 as u128).pow(target_collection.decimal_points)829 };830831 Self::add_fungible_item(item)?;832 },833 CollectionMode::ReFungible(_, _) => {834835 836 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");837838 let mut owner_list = Vec::new();839 let value = (10 as u128).pow(target_collection.decimal_points);840 owner_list.push(Ownership {owner: owner.clone(), fraction: value});841842 let item = ReFungibleItemType {843 collection: collection_id,844 owner: owner_list,845 data: properties.clone()846 };847848 Self::add_refungible_item(item)?;849 },850 _ => { ensure!(1 == 0,"just error"); }851852 };853854 855 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));856857 Ok(())858 }859860 861 862 863 864 865 866 867 868 869 870 871 872 873 #[weight =874 (170_000_000 as Weight)875 .saturating_add(RocksDbWeight::get().reads(9 as Weight))876 .saturating_add(RocksDbWeight::get().writes(7 as Weight))]877 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {878879 let sender = ensure_signed(origin)?;880 Self::collection_exists(collection_id)?;881882 883 let target_collection = <Collection<T>>::get(collection_id);884 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||885 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),886 "Only item owner, collection owner and admins can modify item");887888 if target_collection.access == AccessMode::WhiteList {889 Self::check_white_list(collection_id, &sender)?;890 }891892 match target_collection.mode893 {894 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,895 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,896 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,897 _ => ()898 };899900 901 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));902903 Ok(())904 }905906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 #[weight =930 (125_000_000 as Weight)931 .saturating_add(RocksDbWeight::get().reads(7 as Weight))932 .saturating_add(RocksDbWeight::get().writes(7 as Weight))]933 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {934935 let sender = ensure_signed(origin)?;936937 938 let target_collection = <Collection<T>>::get(collection_id);939 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||940 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),941 "Only item owner, collection owner and admins can modify item");942943 if target_collection.access == AccessMode::WhiteList {944 Self::check_white_list(collection_id, &sender)?;945 Self::check_white_list(collection_id, &recipient)?;946 }947948 match target_collection.mode949 {950 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,951 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,952 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,953 _ => ()954 };955956 Ok(())957 }958959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 #[weight =975 (45_000_000 as Weight)976 .saturating_add(RocksDbWeight::get().reads(3 as Weight))977 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]978 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {979980 let sender = ensure_signed(origin)?;981982 983 let target_collection = <Collection<T>>::get(collection_id);984 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||985 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),986 "Only item owner, collection owner and admins can approve");987988 if target_collection.access == AccessMode::WhiteList {989 Self::check_white_list(collection_id, &sender)?;990 Self::check_white_list(collection_id, &approved)?;991 }992993 994 let amount = 100000000;995996 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));997 if list_exists {998999 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1000 let item_contains = list.iter().any(|i| i.approved == approved);10011002 if !item_contains {1003 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1004 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1005 }1006 } else {10071008 let mut list = Vec::new();1009 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1010 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1011 }10121013 Ok(())1014 }1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 #[weight =1036 (150_000_000 as Weight)1037 .saturating_add(RocksDbWeight::get().reads(9 as Weight))1038 .saturating_add(RocksDbWeight::get().writes(8 as Weight))]1039 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10401041 let sender = ensure_signed(origin)?;1042 let mut appoved_transfer = false;10431044 1045 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1046 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1047 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1048 if opt_item.is_some()1049 {1050 appoved_transfer = true;1051 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1052 }1053 }10541055 1056 let target_collection = <Collection<T>>::get(collection_id);1057 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1058 "Only item owner, collection owner and admins can modify items");10591060 if target_collection.access == AccessMode::WhiteList {1061 Self::check_white_list(collection_id, &sender)?;1062 Self::check_white_list(collection_id, &recipient)?;1063 }10641065 1066 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1067 .into_iter().filter(|i| i.approved != sender.clone()).collect();1068 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);106910701071 match target_collection.mode1072 {1073 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1074 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1075 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1076 _ => ()1077 };10781079 Ok(())1080 }10811082 1083 #[weight = 0]1084 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10851086 1087 1088 1089 10901091 10921093 10941095 Ok(())1096 }10971098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 #[weight =1111 (33_000_000 as Weight)1112 .saturating_add(RocksDbWeight::get().reads(2 as Weight))1113 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]1114 pub fn set_offchain_schema(1115 origin,1116 collection_id: u64,1117 schema: Vec<u8>1118 ) -> DispatchResult {1119 let sender = ensure_signed(origin)?;1120 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11211122 let mut target_collection = <Collection<T>>::get(collection_id);1123 target_collection.offchain_schema = schema;1124 <Collection<T>>::insert(collection_id, target_collection);11251126 Ok(())1127 }11281129 1130 #[weight = 0]1131 pub fn set_chain_limits(1132 origin,1133 limits: ChainLimits1134 ) -> DispatchResult {1135 ensure_root(origin)?;1136 <ChainLimit>::put(limits);1137 Ok(())1138 } 1139 }1140}11411142impl<T: Trait> Module<T> {1143 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1144 let current_index = <ItemListIndex>::get(item.collection)1145 .checked_add(1)1146 .expect("Item list index id error");1147 let itemcopy = item.clone();1148 let owner = item.owner.clone();1149 let value = item.value as u64;11501151 Self::add_token_index(item.collection, current_index, owner.clone())?;11521153 <ItemListIndex>::insert(item.collection, current_index);1154 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11551156 1157 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1158 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1159 1160 1161 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1162 .checked_add(value)1163 .unwrap();1164 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11651166 Ok(())1167 }11681169 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1170 let current_index = <ItemListIndex>::get(item.collection)1171 .checked_add(1)1172 .expect("Item list index id error");1173 let itemcopy = item.clone();11741175 let value = item.owner.first().unwrap().fraction as u64;1176 let owner = item.owner.first().unwrap().owner.clone();11771178 Self::add_token_index(item.collection, current_index, owner.clone())?;11791180 <ItemListIndex>::insert(item.collection, current_index);1181 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11821183 1184 let block_number: T::BlockNumber = 0.into();1185 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11861187 1188 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1189 .checked_add(value)1190 .unwrap();1191 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11921193 Ok(())1194 }11951196 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1197 let current_index = <ItemListIndex>::get(item.collection)1198 .checked_add(1)1199 .expect("Item list index id error");12001201 let item_owner = item.owner.clone();1202 let collection_id = item.collection.clone();1203 Self::add_token_index(collection_id, current_index, item.owner.clone())?;12041205 <ItemListIndex>::insert(collection_id, current_index);1206 <NftItemList<T>>::insert(collection_id, current_index, item);12071208 1209 let block_number: T::BlockNumber = 0.into();1210 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12111212 1213 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1214 .checked_add(1)1215 .unwrap();1216 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12171218 Ok(())1219 }12201221 fn burn_refungible_item(1222 collection_id: u64,1223 item_id: u64,1224 owner: T::AccountId,1225 ) -> DispatchResult {1226 ensure!(1227 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1228 "Item does not exists"1229 );1230 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1231 let item = collection1232 .owner1233 .iter()1234 .filter(|&i| i.owner == owner)1235 .next()1236 .unwrap();1237 Self::remove_token_index(collection_id, item_id, owner.clone())?;12381239 1240 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12411242 1243 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1244 .checked_sub(item.fraction as u64)1245 .unwrap();1246 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12471248 <ReFungibleItemList<T>>::remove(collection_id, item_id);12491250 Ok(())1251 }12521253 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1254 ensure!(1255 <NftItemList<T>>::contains_key(collection_id, item_id),1256 "Item does not exists"1257 );1258 let item = <NftItemList<T>>::get(collection_id, item_id);1259 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12601261 1262 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12631264 1265 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1266 .checked_sub(1)1267 .unwrap();1268 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1269 <NftItemList<T>>::remove(collection_id, item_id);12701271 Ok(())1272 }12731274 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1275 ensure!(1276 <FungibleItemList<T>>::contains_key(collection_id, item_id),1277 "Item does not exists"1278 );1279 let item = <FungibleItemList<T>>::get(collection_id, item_id);1280 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12811282 1283 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12841285 1286 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1287 .checked_sub(item.value as u64)1288 .unwrap();1289 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12901291 <FungibleItemList<T>>::remove(collection_id, item_id);12921293 Ok(())1294 }12951296 fn collection_exists(collection_id: u64) -> DispatchResult {1297 ensure!(1298 <Collection<T>>::contains_key(collection_id),1299 "This collection does not exist"1300 );1301 Ok(())1302 }13031304 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1305 Self::collection_exists(collection_id)?;13061307 let target_collection = <Collection<T>>::get(collection_id);1308 ensure!(1309 subject == target_collection.owner,1310 "You do not own this collection"1311 );13121313 Ok(())1314 }13151316 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1317 let target_collection = <Collection<T>>::get(collection_id);1318 let mut result: bool = subject == target_collection.owner;1319 let exists = <AdminList<T>>::contains_key(collection_id);13201321 if !result & exists {1322 if <AdminList<T>>::get(collection_id).contains(&subject) {1323 result = true1324 }1325 }13261327 result1328 }13291330 fn check_owner_or_admin_permissions(1331 collection_id: u64,1332 subject: T::AccountId,1333 ) -> DispatchResult {1334 Self::collection_exists(collection_id)?;1335 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13361337 ensure!(1338 result,1339 "You do not have permissions to modify this collection"1340 );1341 Ok(())1342 }13431344 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1345 let target_collection = <Collection<T>>::get(collection_id);13461347 match target_collection.mode {1348 CollectionMode::NFT(_) => {1349 <NftItemList<T>>::get(collection_id, item_id).owner == subject1350 }1351 CollectionMode::Fungible(_) => {1352 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1353 }1354 CollectionMode::ReFungible(_, _) => {1355 <ReFungibleItemList<T>>::get(collection_id, item_id)1356 .owner1357 .iter()1358 .any(|i| i.owner == subject)1359 }1360 CollectionMode::Invalid => false,1361 }1362 }13631364 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1365 let mes = "Address is not in white list";1366 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1367 let wl = <WhiteList<T>>::get(collection_id);1368 ensure!(wl.contains(address), mes);13691370 Ok(())1371 }13721373 fn transfer_fungible(1374 collection_id: u64,1375 item_id: u64,1376 value: u64,1377 owner: T::AccountId,1378 new_owner: T::AccountId,1379 ) -> DispatchResult {1380 ensure!(1381 <FungibleItemList<T>>::contains_key(collection_id, item_id),1382 "Item not exists"1383 );13841385 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1386 let amount = full_item.value;13871388 ensure!(amount >= value.into(), "Item balance not enouth");13891390 1391 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1392 .checked_sub(value)1393 .unwrap();1394 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);13951396 let mut new_owner_account_id = 0;1397 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1398 if new_owner_items.len() > 0 {1399 new_owner_account_id = new_owner_items[0];1400 }14011402 let val64 = value.into();14031404 1405 if amount == val64 && new_owner_account_id == 0 {1406 1407 1408 let mut new_full_item = full_item.clone();1409 new_full_item.owner = new_owner.clone();1410 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14111412 1413 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1414 .checked_add(value)1415 .unwrap();1416 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14171418 1419 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1420 } else {1421 let mut new_full_item = full_item.clone();1422 new_full_item.value -= val64;14231424 1425 if new_owner_account_id > 0 {1426 1427 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1428 item.value += val64;14291430 1431 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1432 .checked_add(value)1433 .unwrap();1434 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14351436 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1437 } else {1438 1439 let item = FungibleItemType {1440 collection: collection_id,1441 owner: new_owner.clone(),1442 value: val64,1443 };14441445 Self::add_fungible_item(item)?;1446 }14471448 if amount == val64 {1449 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14501451 1452 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1453 <FungibleItemList<T>>::remove(collection_id, item_id);1454 }14551456 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1457 }14581459 Ok(())1460 }14611462 fn transfer_refungible(1463 collection_id: u64,1464 item_id: u64,1465 value: u64,1466 owner: T::AccountId,1467 new_owner: T::AccountId,1468 ) -> DispatchResult {1469 ensure!(1470 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1471 "Item not exists"1472 );14731474 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1475 let item = full_item1476 .owner1477 .iter()1478 .filter(|i| i.owner == owner)1479 .next()1480 .unwrap();1481 let amount = item.fraction;14821483 ensure!(amount >= value.into(), "Item balance not enouth");14841485 1486 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1487 .checked_sub(value)1488 .unwrap();1489 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14901491 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1492 .checked_add(value)1493 .unwrap();1494 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14951496 let old_owner = item.owner.clone();1497 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1498 let val64 = value.into();14991500 1501 if amount == val64 && !new_owner_has_account {1502 1503 1504 let mut new_full_item = full_item.clone();1505 new_full_item1506 .owner1507 .iter_mut()1508 .find(|i| i.owner == owner)1509 .unwrap()1510 .owner = new_owner.clone();1511 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15121513 1514 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1515 } else {1516 let mut new_full_item = full_item.clone();1517 new_full_item1518 .owner1519 .iter_mut()1520 .find(|i| i.owner == owner)1521 .unwrap()1522 .fraction -= val64;15231524 1525 if new_owner_has_account {1526 1527 new_full_item1528 .owner1529 .iter_mut()1530 .find(|i| i.owner == new_owner)1531 .unwrap()1532 .fraction += val64;1533 } else {1534 1535 new_full_item.owner.push(Ownership {1536 owner: new_owner.clone(),1537 fraction: val64,1538 });1539 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1540 }15411542 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1543 }15441545 Ok(())1546 }15471548 fn transfer_nft(1549 collection_id: u64,1550 item_id: u64,1551 sender: T::AccountId,1552 new_owner: T::AccountId,1553 ) -> DispatchResult {1554 ensure!(1555 <NftItemList<T>>::contains_key(collection_id, item_id),1556 "Item not exists"1557 );15581559 let mut item = <NftItemList<T>>::get(collection_id, item_id);15601561 ensure!(1562 sender == item.owner,1563 "sender parameter and item owner must be equal"1564 );15651566 1567 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1568 .checked_sub(1)1569 .unwrap();1570 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15711572 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1573 .checked_add(1)1574 .unwrap();1575 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15761577 1578 let old_owner = item.owner.clone();1579 item.owner = new_owner.clone();1580 <NftItemList<T>>::insert(collection_id, item_id, item);15811582 1583 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15841585 1586 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1587 Ok(())1588 }15891590 fn init_collection(item: &CollectionType<T::AccountId>) {1591 1592 assert!(1593 item.decimal_points <= 4,1594 "decimal_points parameter must be lower than 4"1595 );1596 assert!(1597 item.name.len() <= 64,1598 "Collection name can not be longer than 63 char"1599 );1600 assert!(1601 item.name.len() <= 256,1602 "Collection description can not be longer than 255 char"1603 );1604 assert!(1605 item.token_prefix.len() <= 16,1606 "Token prefix can not be longer than 15 char"1607 );16081609 1610 let next_id = CreatedCollectionCount::get()1611 .checked_add(1)1612 .expect("collection id error");16131614 CreatedCollectionCount::put(next_id);1615 }16161617 fn init_nft_token(item: &NftItemType<T::AccountId>) {1618 let current_index = <ItemListIndex>::get(item.collection)1619 .checked_add(1)1620 .expect("Item list index id error");16211622 let item_owner = item.owner.clone();1623 let collection_id = item.collection.clone();1624 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16251626 <ItemListIndex>::insert(collection_id, current_index);16271628 1629 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1630 .checked_add(1)1631 .unwrap();1632 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1633 }16341635 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1636 let current_index = <ItemListIndex>::get(item.collection)1637 .checked_add(1)1638 .expect("Item list index id error");1639 let owner = item.owner.clone();1640 let value = item.value as u64;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 init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1654 let current_index = <ItemListIndex>::get(item.collection)1655 .checked_add(1)1656 .expect("Item list index id error");16571658 let value = item.owner.first().unwrap().fraction as u64;1659 let owner = item.owner.first().unwrap().owner.clone();16601661 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16621663 <ItemListIndex>::insert(item.collection, current_index);16641665 1666 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1667 .checked_add(value)1668 .unwrap();1669 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1670 }16711672 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16731674 1675 if <AccountItemCount<T>>::contains_key(owner.clone()) {16761677 1678 let count = <AccountItemCount<T>>::get(owner.clone());1679 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16801681 <AccountItemCount<T>>::insert(owner.clone(), 1682 count.checked_add(1).unwrap());1683 }1684 else {1685 <AccountItemCount<T>>::insert(owner.clone(), 1);1686 }16871688 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1689 if list_exists {1690 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1691 let item_contains = list.contains(&item_index.clone());16921693 if !item_contains {1694 list.push(item_index.clone());1695 }16961697 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1698 } else {1699 let mut itm = Vec::new();1700 itm.push(item_index.clone());1701 <AddressTokens<T>>::insert(collection_id, owner, itm);1702 1703 }17041705 Ok(())1706 }17071708 fn remove_token_index(1709 collection_id: u64,1710 item_index: u64,1711 owner: T::AccountId,1712 ) -> DispatchResult {17131714 1715 <AccountItemCount<T>>::insert(owner.clone(), 1716 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());171717181719 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1720 if list_exists {1721 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1722 let item_contains = list.contains(&item_index.clone());17231724 if item_contains {1725 list.retain(|&item| item != item_index);1726 <AddressTokens<T>>::insert(collection_id, owner, list);1727 }1728 }17291730 Ok(())1731 }17321733 fn move_token_index(1734 collection_id: u64,1735 item_index: u64,1736 old_owner: T::AccountId,1737 new_owner: T::AccountId,1738 ) -> DispatchResult {1739 Self::remove_token_index(collection_id, item_index, old_owner)?;1740 Self::add_token_index(collection_id, item_index, new_owner)?;17411742 Ok(())1743 }1744}1745174617471748174917501751pub type Multiplier = FixedU128;17521753type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1754 <T as system::Trait>::AccountId,1755>>::Balance;1756type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1757 <T as system::Trait>::AccountId,1758>>::NegativeImbalance;1759176017611762#[derive(Encode, Decode, Clone, Eq, PartialEq)]1763pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1764 #[codec(compact)] BalanceOf<T>,1765);17661767impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1768 for ChargeTransactionPayment<T>1769{1770 #[cfg(feature = "std")]1771 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1772 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1773 }1774 #[cfg(not(feature = "std"))]1775 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1776 Ok(())1777 }1778}17791780impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1781where1782 T::Call:1783 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1784 BalanceOf<T>: Send + Sync + FixedPointOperand,1785{1786 1787 pub fn from(fee: BalanceOf<T>) -> Self {1788 Self(fee)1789 }17901791 pub fn traditional_fee(1792 len: usize,1793 info: &DispatchInfoOf<T::Call>,1794 tip: BalanceOf<T>,1795 ) -> BalanceOf<T>1796 where1797 T::Call: Dispatchable<Info = DispatchInfo>,1798 {1799 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1800 }18011802 fn withdraw_fee(1803 &self,1804 who: &T::AccountId,1805 call: &T::Call,1806 info: &DispatchInfoOf<T::Call>,1807 len: usize,1808 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1809 let tip = self.0;18101811 1812 1813 let fee = match call.is_sub_type() {1814 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1815 _ => Self::traditional_fee(len, info, tip), 1816 1817 };18181819 1820 1821 let sponsor: T::AccountId = match call.is_sub_type() {1822 Some(Call::create_item(collection_id, _properties, _owner)) => {1823 <Collection<T>>::get(collection_id).sponsor1824 }1825 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1826 let _collection_mode = <Collection<T>>::get(collection_id).mode;18271828 1829 let sponsor_transfer = match _collection_mode {1830 CollectionMode::NFT(_) => {1831 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1832 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1833 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1834 if block_number >= limit_time {1835 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1836 true1837 }1838 else {1839 false1840 }1841 }1842 CollectionMode::Fungible(_) => {1843 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1844 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1845 if basket.iter().any(|i| i.address == _new_owner.clone())1846 {1847 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1848 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1849 if block_number >= limit_time {1850 basket.retain(|x| x.address == item.address);1851 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1852 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1853 true1854 }1855 else {1856 false1857 }1858 }1859 else {1860 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1861 true1862 }1863 }1864 CollectionMode::ReFungible(_, _) => {1865 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1866 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1867 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1868 if block_number >= limit_time {1869 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1870 true1871 } else {1872 false1873 }1874 }1875 _ => {1876 false1877 },1878 };18791880 if !sponsor_transfer {1881 T::AccountId::default()1882 } else {1883 <Collection<T>>::get(collection_id).sponsor1884 }1885 }18861887 _ => T::AccountId::default(),1888 };18891890 let mut who_pays_fee: T::AccountId = sponsor.clone();1891 if sponsor == T::AccountId::default() {1892 who_pays_fee = who.clone();1893 }18941895 1896 if fee.is_zero() {1897 return Ok((fee, None));1898 }18991900 match <T as transaction_payment::Trait>::Currency::withdraw(1901 &who_pays_fee,1902 fee,1903 if tip.is_zero() {1904 WithdrawReason::TransactionPayment.into()1905 } else {1906 WithdrawReason::TransactionPayment | WithdrawReason::Tip1907 },1908 ExistenceRequirement::KeepAlive,1909 ) {1910 Ok(imbalance) => Ok((fee, Some(imbalance))),1911 Err(_) => Err(InvalidTransaction::Payment.into()),1912 }1913 }1914}19151916impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1917 for ChargeTransactionPayment<T>1918where1919 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1920 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1921{1922 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1923 type AccountId = T::AccountId;1924 type Call = T::Call;1925 type AdditionalSigned = ();1926 type Pre = (1927 BalanceOf<T>,1928 Self::AccountId,1929 Option<NegativeImbalanceOf<T>>,1930 BalanceOf<T>,1931 );1932 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1933 Ok(())1934 }19351936 fn validate(1937 &self,1938 who: &Self::AccountId,1939 call: &Self::Call,1940 info: &DispatchInfoOf<Self::Call>,1941 len: usize,1942 ) -> TransactionValidity {1943 let (fee, _) = self.withdraw_fee(who, call, info, len)?;19441945 let mut r = ValidTransaction::default();1946 1947 1948 r.priority = fee.saturated_into::<TransactionPriority>();1949 Ok(r)1950 }19511952 fn pre_dispatch(1953 self,1954 who: &Self::AccountId,1955 call: &Self::Call,1956 info: &DispatchInfoOf<Self::Call>,1957 len: usize,1958 ) -> Result<Self::Pre, TransactionValidityError> {1959 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1960 Ok((self.0, who.clone(), imbalance, fee))1961 }19621963 fn post_dispatch(1964 pre: Self::Pre,1965 info: &DispatchInfoOf<Self::Call>,1966 post_info: &PostDispatchInfoOf<Self::Call>,1967 len: usize,1968 _result: &DispatchResult,1969 ) -> Result<(), TransactionValidityError> {1970 let (tip, who, imbalance, fee) = pre;1971 if let Some(payed) = imbalance {1972 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1973 len as u32, info, post_info, tip,1974 );1975 let refund = fee.saturating_sub(actual_fee);1976 let actual_payment =1977 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1978 &who, refund,1979 ) {1980 Ok(refund_imbalance) => {1981 1982 1983 match payed.offset(refund_imbalance) {1984 Ok(actual_payment) => actual_payment,1985 Err(_) => return Err(InvalidTransaction::Payment.into()),1986 }1987 }1988 1989 1990 Err(_) => payed,1991 };1992 let imbalances = actual_payment.split(tip);1993 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1994 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1995 );1996 }1997 Ok(())1998 }1999}200020012002