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, fail,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 NFT,57 58 Fungible(u32),59 60 ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64 fn into(self) -> u8 {65 match self {66 CollectionMode::Invalid => 0,67 CollectionMode::NFT => 1,68 CollectionMode::Fungible(_) => 2,69 CollectionMode::ReFungible(_) => 3,70 }71 }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77 Normal,78 WhiteList,79}80impl Default for AccessMode {81 fn default() -> Self {82 Self::Normal83 }84}8586impl Default for CollectionMode {87 fn default() -> Self {88 Self::Invalid89 }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95 pub owner: AccountId,96 pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102 pub owner: AccountId,103 pub mode: CollectionMode,104 pub access: AccessMode,105 pub decimal_points: u32,106 pub name: Vec<u16>, 107 pub description: Vec<u16>, 108 pub token_prefix: Vec<u8>, 109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, 112 pub unconfirmed_sponsor: AccountId, 113 pub variable_on_chain_schema: Vec<u8>, 114 pub const_on_chain_schema: Vec<u8>, 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 const_data: Vec<u8>,130 pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136 pub collection: u64,137 pub owner: AccountId,138 pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144 pub collection: u64,145 pub owner: Vec<Ownership<AccountId>>,146 pub const_data: Vec<u8>,147 pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153 pub approved: AccountId,154 pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160 pub sender: AccountId,161 pub recipient: AccountId,162 pub collection_id: u64,163 pub item_id: u64,164 pub amount: u64,165 pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171 pub address: AccountId,172 pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178 pub collection_numbers_limit: u64,179 pub account_token_ownership_limit: u64,180 pub collections_admins_limit: u64,181 pub custom_data_limit: u32,182183 184 pub nft_sponsor_transfer_timeout: u32,185 pub fungible_sponsor_transfer_timeout: u32,186 pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190 fn create_collection() -> Weight;191 fn destroy_collection() -> Weight;192 fn add_to_white_list() -> Weight;193 fn remove_from_white_list() -> Weight;194 fn set_public_access_mode() -> Weight;195 fn set_mint_permission() -> Weight;196 fn change_collection_owner() -> Weight;197 fn add_collection_admin() -> Weight;198 fn remove_collection_admin() -> Weight;199 fn set_collection_sponsor() -> Weight;200 fn confirm_sponsorship() -> Weight;201 fn remove_collection_sponsor() -> Weight;202 fn create_item(s: usize) -> Weight;203 fn burn_item() -> Weight;204 fn transfer() -> Weight;205 fn approve() -> Weight;206 fn transfer_from() -> Weight;207 fn set_offchain_schema() -> Weight;208 fn set_const_on_chain_schema() -> Weight;209 fn set_variable_on_chain_schema() -> Weight;210 fn set_variable_meta_data() -> Weight;211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216 pub const_data: Vec<u8>,217 pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228 pub const_data: Vec<u8>,229 pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235 NFT(CreateNftData),236 Fungible(CreateFungibleData),237 ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241 pub fn len(&self) -> usize {242 let len = match self {243 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245 _ => 0246 };247 248 return len;249 }250}251252pub trait Trait: system::Trait + Sized {253 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;254255 256 type WeightInfo: WeightInfo;257}258259#[cfg(feature = "runtime-benchmarks")]260mod benchmarking;261262263264decl_storage! {265 trait Store for Module<T: Trait> as Nft {266267 268 NextCollectionID: u64;269 CreatedCollectionCount: u64;270 ChainVersion: u64;271 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;272273 274 pub ChainLimit get(fn chain_limit) config(): ChainLimits;275276 277 CollectionCount: u64;278 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;279280 281 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;282 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;283 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;284285 286 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;287288 289 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;290291 292 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;293 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;294 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;295296 297 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;298299 300 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;301 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>>;302 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;303304 305 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;306 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;307 }308 add_extra_genesis {309 build(|config: &GenesisConfig<T>| {310 311 for (_num, _c) in &config.collection {312 <Module<T>>::init_collection(_c);313 }314315 for (_num, _q, _i) in &config.nft_item_id {316 <Module<T>>::init_nft_token(_i);317 }318319 for (_num, _q, _i) in &config.fungible_item_id {320 <Module<T>>::init_fungible_token(_i);321 }322323 for (_num, _q, _i) in &config.refungible_item_id {324 <Module<T>>::init_refungible_token(_i);325 }326 })327 }328}329330decl_event!(331 pub enum Event<T>332 where333 AccountId = <T as system::Trait>::AccountId,334 {335 336 337 338 339 340 341 342 343 344 Created(u64, u8, AccountId),345346 347 348 349 350 351 352 353 ItemCreated(u64, u64),354355 356 357 358 359 360 361 362 ItemDestroyed(u64, u64),363 }364);365366decl_module! {367 pub struct Module<T: Trait> for enum Call where origin: T::Origin {368369 fn deposit_event() = default;370371 fn on_initialize(now: T::BlockNumber) -> Weight {372373 if ChainVersion::get() < 2374 {375 let value = NextCollectionID::get();376 CreatedCollectionCount::put(value);377 ChainVersion::put(2);378 }379380 0381 }382383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 #[weight = T::WeightInfo::create_collection()]400 pub fn create_collection(origin,401 collection_name: Vec<u16>,402 collection_description: Vec<u16>,403 token_prefix: Vec<u8>,404 mode: CollectionMode) -> DispatchResult {405406 407 let who = ensure_signed(origin)?;408409 let decimal_points = match mode {410 CollectionMode::Fungible(points) => points,411 CollectionMode::ReFungible(points) => points,412 _ => 0413 };414415 416 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");417418 419 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");420421 let mut name = collection_name.to_vec();422 name.push(0);423 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");424425 let mut description = collection_description.to_vec();426 description.push(0);427 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");428429 let mut prefix = token_prefix.to_vec();430 prefix.push(0);431 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");432433 434 let next_id = CreatedCollectionCount::get()435 .checked_add(1)436 .expect("collection id error");437438 439 let total = CollectionCount::get()440 .checked_add(1)441 .expect("collection counter error");442443 CreatedCollectionCount::put(next_id);444 CollectionCount::put(total);445446 447 let new_collection = CollectionType {448 owner: who.clone(),449 name: name,450 mode: mode.clone(),451 mint_mode: false,452 access: AccessMode::Normal,453 description: description,454 decimal_points: decimal_points,455 token_prefix: prefix,456 offchain_schema: Vec::new(),457 sponsor: T::AccountId::default(),458 unconfirmed_sponsor: T::AccountId::default(),459 variable_on_chain_schema: Vec::new(),460 const_on_chain_schema: Vec::new(),461 };462463 464 <Collection<T>>::insert(next_id, new_collection);465466 467 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));468469 Ok(())470 }471472 473 474 475 476 477 478 479 480 481 #[weight = T::WeightInfo::destroy_collection()]482 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {483484 let sender = ensure_signed(origin)?;485 Self::check_owner_permissions(collection_id, sender)?;486487 <AddressTokens<T>>::remove_prefix(collection_id);488 <ApprovedList<T>>::remove_prefix(collection_id);489 <Balance<T>>::remove_prefix(collection_id);490 <ItemListIndex>::remove(collection_id);491 <AdminList<T>>::remove(collection_id);492 <Collection<T>>::remove(collection_id);493 <WhiteList<T>>::remove(collection_id);494495 <NftItemList<T>>::remove_prefix(collection_id);496 <FungibleItemList<T>>::remove_prefix(collection_id);497 <ReFungibleItemList<T>>::remove_prefix(collection_id);498499 <NftTransferBasket<T>>::remove_prefix(collection_id);500 <FungibleTransferBasket<T>>::remove_prefix(collection_id);501 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);502503 if CollectionCount::get() > 0504 {505 506 let total = CollectionCount::get()507 .checked_sub(1)508 .expect("collection counter error");509510 CollectionCount::put(total);511 }512513 Ok(())514 }515516 517 518 519 520 521 522 523 524 525 526 527 528 #[weight = T::WeightInfo::add_to_white_list()]529 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{530531 let sender = ensure_signed(origin)?;532 Self::check_owner_or_admin_permissions(collection_id, sender)?;533534 let mut white_list_collection: Vec<T::AccountId>;535 if <WhiteList<T>>::contains_key(collection_id) {536 white_list_collection = <WhiteList<T>>::get(collection_id);537 if !white_list_collection.contains(&address.clone())538 {539 white_list_collection.push(address.clone());540 }541 }542 else {543 white_list_collection = Vec::new();544 white_list_collection.push(address.clone());545 }546547 <WhiteList<T>>::insert(collection_id, white_list_collection);548 Ok(())549 }550551 552 553 554 555 556 557 558 559 560 561 562 563 #[weight = T::WeightInfo::remove_from_white_list()]564 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{565566 let sender = ensure_signed(origin)?;567 Self::check_owner_or_admin_permissions(collection_id, sender)?;568569 if <WhiteList<T>>::contains_key(collection_id) {570 let mut white_list_collection = <WhiteList<T>>::get(collection_id);571 if white_list_collection.contains(&address.clone())572 {573 white_list_collection.retain(|i| *i != address.clone());574 <WhiteList<T>>::insert(collection_id, white_list_collection);575 }576 }577578 Ok(())579 }580581 582 583 584 585 586 587 588 589 590 591 592 #[weight = T::WeightInfo::set_public_access_mode()]593 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult594 {595 let sender = ensure_signed(origin)?;596597 Self::check_owner_permissions(collection_id, sender)?;598 let mut target_collection = <Collection<T>>::get(collection_id);599 target_collection.access = mode;600 <Collection<T>>::insert(collection_id, target_collection);601602 Ok(())603 }604605 606 607 608 609 610 611 612 613 614 615 616 617 618 #[weight = T::WeightInfo::set_mint_permission()]619 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult620 {621 let sender = ensure_signed(origin)?;622623 Self::check_owner_permissions(collection_id, sender)?;624 let mut target_collection = <Collection<T>>::get(collection_id);625 target_collection.mint_mode = mint_permission;626 <Collection<T>>::insert(collection_id, target_collection);627628 Ok(())629 }630631 632 633 634 635 636 637 638 639 640 641 642 #[weight = T::WeightInfo::change_collection_owner()]643 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {644645 let sender = ensure_signed(origin)?;646 Self::check_owner_permissions(collection_id, sender)?;647 let mut target_collection = <Collection<T>>::get(collection_id);648 target_collection.owner = new_owner;649 <Collection<T>>::insert(collection_id, target_collection);650651 Ok(())652 }653654 655 656 657 658 659 660 661 662 663 664 665 666 667 #[weight = T::WeightInfo::add_collection_admin()]668 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {669670 let sender = ensure_signed(origin)?;671 Self::check_owner_or_admin_permissions(collection_id, sender)?;672 let mut admin_arr: Vec<T::AccountId> = Vec::new();673674 if <AdminList<T>>::contains_key(collection_id)675 {676 admin_arr = <AdminList<T>>::get(collection_id);677 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");678 }679680 681 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");682683 admin_arr.push(new_admin_id);684 <AdminList<T>>::insert(collection_id, admin_arr);685686 Ok(())687 }688689 690 691 692 693 694 695 696 697 698 699 700 701 #[weight = T::WeightInfo::remove_collection_admin()]702 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {703704 let sender = ensure_signed(origin)?;705 Self::check_owner_or_admin_permissions(collection_id, sender)?;706707 if <AdminList<T>>::contains_key(collection_id)708 {709 let mut admin_arr = <AdminList<T>>::get(collection_id);710 admin_arr.retain(|i| *i != account_id);711 <AdminList<T>>::insert(collection_id, admin_arr);712 }713714 Ok(())715 }716717 718 719 720 721 722 723 724 725 726 #[weight = T::WeightInfo::set_collection_sponsor()]727 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {728729 let sender = ensure_signed(origin)?;730 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");731732 let mut target_collection = <Collection<T>>::get(collection_id);733 ensure!(sender == target_collection.owner, "You do not own this collection");734735 target_collection.unconfirmed_sponsor = new_sponsor;736 <Collection<T>>::insert(collection_id, target_collection);737738 Ok(())739 }740741 742 743 744 745 746 747 748 #[weight = T::WeightInfo::confirm_sponsorship()]749 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {750751 let sender = ensure_signed(origin)?;752 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");753754 let mut target_collection = <Collection<T>>::get(collection_id);755 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");756757 target_collection.sponsor = target_collection.unconfirmed_sponsor;758 target_collection.unconfirmed_sponsor = T::AccountId::default();759 <Collection<T>>::insert(collection_id, target_collection);760761 Ok(())762 }763764 765 766 767 768 769 770 771 772 773 #[weight = T::WeightInfo::remove_collection_sponsor()]774 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {775776 let sender = ensure_signed(origin)?;777 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");778779 let mut target_collection = <Collection<T>>::get(collection_id);780 ensure!(sender == target_collection.owner, "You do not own this collection");781782 target_collection.sponsor = T::AccountId::default();783 <Collection<T>>::insert(collection_id, target_collection);784785 Ok(())786 }787788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811812 #[weight = T::WeightInfo::create_item(data.len())]813 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {814815 let sender = ensure_signed(origin)?;816 Self::collection_exists(collection_id)?;817 let target_collection = <Collection<T>>::get(collection_id);818819 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {820 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");821 Self::check_white_list(collection_id, &owner)?;822 Self::check_white_list(collection_id, &sender)?;823 }824825 match target_collection.mode826 {827 CollectionMode::NFT => {828 if let CreateItemData::NFT(data) = data {829 830 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");831 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");832 833 834 let item = NftItemType {835 collection: collection_id,836 owner: owner,837 const_data: data.const_data.clone(),838 variable_data: data.variable_data.clone() 839 };840 841 Self::add_nft_item(item)?;842 843 } else {844 fail!("Not NFT item data used to mint in NFT collection.");845 }846 },847 CollectionMode::Fungible(_) => {848 if let CreateItemData::Fungible(_) = data {849 850 let item = FungibleItemType {851 collection: collection_id,852 owner: owner,853 value: (10 as u128).pow(target_collection.decimal_points)854 };855 856 Self::add_fungible_item(item)?;857 } else {858 fail!("Not Fungible item data used to mint in Fungible collection.");859 }860 },861 CollectionMode::ReFungible(_) => {862 if let CreateItemData::ReFungible(data) = data {863 864 865 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");866 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");867 868 let mut owner_list = Vec::new();869 let value = (10 as u128).pow(target_collection.decimal_points);870 owner_list.push(Ownership {owner: owner.clone(), fraction: value});871 872 let item = ReFungibleItemType {873 collection: collection_id,874 owner: owner_list,875 const_data: data.const_data.clone(),876 variable_data: data.variable_data.clone() 877 };878 879 Self::add_refungible_item(item)?;880 } else {881 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");882 }883 },884 _ => { ensure!(1 == 0,"Unexpected collection type."); }885 };886887 888 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));889890 Ok(())891 }892893 894 895 896 897 898 899 900 901 902 903 904 905 906 #[weight = T::WeightInfo::burn_item()]907 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {908909 let sender = ensure_signed(origin)?;910 Self::collection_exists(collection_id)?;911912 913 let target_collection = <Collection<T>>::get(collection_id);914 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||915 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),916 "Only item owner, collection owner and admins can modify item");917918 if target_collection.access == AccessMode::WhiteList {919 Self::check_white_list(collection_id, &sender)?;920 }921922 match target_collection.mode923 {924 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,925 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,926 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,927 _ => ()928 };929930 931 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));932933 Ok(())934 }935936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 #[weight = T::WeightInfo::transfer()]960 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {961962 let sender = ensure_signed(origin)?;963964 965 let target_collection = <Collection<T>>::get(collection_id);966 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||967 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),968 "Only item owner, collection owner and admins can modify item");969970 if target_collection.access == AccessMode::WhiteList {971 Self::check_white_list(collection_id, &sender)?;972 Self::check_white_list(collection_id, &recipient)?;973 }974975 match target_collection.mode976 {977 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,978 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,979 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,980 _ => ()981 };982983 Ok(())984 }985986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 #[weight = T::WeightInfo::approve()]1002 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10031004 let sender = ensure_signed(origin)?;10051006 1007 let target_collection = <Collection<T>>::get(collection_id);1008 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1009 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1010 "Only item owner, collection owner and admins can approve");10111012 if target_collection.access == AccessMode::WhiteList {1013 Self::check_white_list(collection_id, &sender)?;1014 Self::check_white_list(collection_id, &approved)?;1015 }10161017 1018 let amount = 100000000;10191020 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1021 if list_exists {10221023 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1024 let item_contains = list.iter().any(|i| i.approved == approved);10251026 if !item_contains {1027 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1028 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1029 }1030 } else {10311032 let mut list = Vec::new();1033 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1034 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1035 }10361037 Ok(())1038 }1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 #[weight = T::WeightInfo::transfer_from()]1060 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10611062 let sender = ensure_signed(origin)?;1063 let mut appoved_transfer = false;10641065 1066 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1067 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1068 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1069 if opt_item.is_some()1070 {1071 appoved_transfer = true;1072 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1073 }1074 }10751076 1077 let target_collection = <Collection<T>>::get(collection_id);1078 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1079 "Only item owner, collection owner and admins can modify items");10801081 if target_collection.access == AccessMode::WhiteList {1082 Self::check_white_list(collection_id, &sender)?;1083 Self::check_white_list(collection_id, &recipient)?;1084 }10851086 1087 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1088 .into_iter().filter(|i| i.approved != sender.clone()).collect();1089 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);109010911092 match target_collection.mode1093 {1094 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1095 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1096 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1097 _ => ()1098 };10991100 Ok(())1101 }11021103 1104 #[weight = 0]1105 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11061107 1108 1109 1110 11111112 11131114 11151116 Ok(())1117 }1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 #[weight = T::WeightInfo::set_variable_meta_data()]1132 pub fn set_variable_meta_data (1133 origin,1134 collection_id: u64,1135 item_id: u64,1136 data: Vec<u8>1137 ) -> DispatchResult {1138 let sender = ensure_signed(origin)?;1139 1140 Self::collection_exists(collection_id)?;11411142 1143 let target_collection = <Collection<T>>::get(collection_id);1144 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1145 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1146 "Only item owner, collection owner and admins can modify item");11471148 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11491150 match target_collection.mode1151 {1152 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1153 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1154 _ => ()1155 };11561157 Ok(())1158 }1159 11601161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 #[weight = T::WeightInfo::set_offchain_schema()]1174 pub fn set_offchain_schema(1175 origin,1176 collection_id: u64,1177 schema: Vec<u8>1178 ) -> DispatchResult {1179 let sender = ensure_signed(origin)?;1180 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11811182 let mut target_collection = <Collection<T>>::get(collection_id);1183 target_collection.offchain_schema = schema;1184 <Collection<T>>::insert(collection_id, target_collection);11851186 Ok(())1187 }11881189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 #[weight = T::WeightInfo::set_const_on_chain_schema()]1202 pub fn set_const_on_chain_schema (1203 origin,1204 collection_id: u64,1205 schema: Vec<u8>1206 ) -> DispatchResult {1207 let sender = ensure_signed(origin)?;1208 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12091210 let mut target_collection = <Collection<T>>::get(collection_id);1211 target_collection.const_on_chain_schema = schema;1212 <Collection<T>>::insert(collection_id, target_collection);12131214 Ok(())1215 }12161217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 #[weight = T::WeightInfo::set_const_on_chain_schema()]1230 pub fn set_variable_on_chain_schema (1231 origin,1232 collection_id: u64,1233 schema: Vec<u8>1234 ) -> DispatchResult {1235 let sender = ensure_signed(origin)?;1236 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12371238 let mut target_collection = <Collection<T>>::get(collection_id);1239 target_collection.variable_on_chain_schema = schema;1240 <Collection<T>>::insert(collection_id, target_collection);12411242 Ok(())1243 }12441245 1246 #[weight = 0]1247 pub fn set_chain_limits(1248 origin,1249 limits: ChainLimits1250 ) -> DispatchResult {1251 ensure_root(origin)?;1252 <ChainLimit>::put(limits);1253 Ok(())1254 } 1255 }1256}12571258impl<T: Trait> Module<T> {1259 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1260 let current_index = <ItemListIndex>::get(item.collection)1261 .checked_add(1)1262 .expect("Item list index id error");1263 let itemcopy = item.clone();1264 let owner = item.owner.clone();1265 let value = item.value as u64;12661267 Self::add_token_index(item.collection, current_index, owner.clone())?;12681269 <ItemListIndex>::insert(item.collection, current_index);1270 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12711272 1273 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1274 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1275 1276 1277 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1278 .checked_add(value)1279 .unwrap();1280 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12811282 Ok(())1283 }12841285 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1286 let current_index = <ItemListIndex>::get(item.collection)1287 .checked_add(1)1288 .expect("Item list index id error");1289 let itemcopy = item.clone();12901291 let value = item.owner.first().unwrap().fraction as u64;1292 let owner = item.owner.first().unwrap().owner.clone();12931294 Self::add_token_index(item.collection, current_index, owner.clone())?;12951296 <ItemListIndex>::insert(item.collection, current_index);1297 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12981299 1300 let block_number: T::BlockNumber = 0.into();1301 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13021303 1304 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1305 .checked_add(value)1306 .unwrap();1307 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13081309 Ok(())1310 }13111312 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1313 let current_index = <ItemListIndex>::get(item.collection)1314 .checked_add(1)1315 .expect("Item list index id error");13161317 let item_owner = item.owner.clone();1318 let collection_id = item.collection.clone();1319 Self::add_token_index(collection_id, current_index, item.owner.clone())?;13201321 <ItemListIndex>::insert(collection_id, current_index);1322 <NftItemList<T>>::insert(collection_id, current_index, item);13231324 1325 let block_number: T::BlockNumber = 0.into();1326 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13271328 1329 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1330 .checked_add(1)1331 .unwrap();1332 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13331334 Ok(())1335 }13361337 fn burn_refungible_item(1338 collection_id: u64,1339 item_id: u64,1340 owner: T::AccountId,1341 ) -> DispatchResult {1342 ensure!(1343 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1344 "Item does not exists"1345 );1346 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1347 let item = collection1348 .owner1349 .iter()1350 .filter(|&i| i.owner == owner)1351 .next()1352 .unwrap();1353 Self::remove_token_index(collection_id, item_id, owner.clone())?;13541355 1356 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));13571358 1359 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1360 .checked_sub(item.fraction as u64)1361 .unwrap();1362 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13631364 <ReFungibleItemList<T>>::remove(collection_id, item_id);13651366 Ok(())1367 }13681369 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1370 ensure!(1371 <NftItemList<T>>::contains_key(collection_id, item_id),1372 "Item does not exists"1373 );1374 let item = <NftItemList<T>>::get(collection_id, item_id);1375 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13761377 1378 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13791380 1381 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1382 .checked_sub(1)1383 .unwrap();1384 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1385 <NftItemList<T>>::remove(collection_id, item_id);13861387 Ok(())1388 }13891390 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1391 ensure!(1392 <FungibleItemList<T>>::contains_key(collection_id, item_id),1393 "Item does not exists"1394 );1395 let item = <FungibleItemList<T>>::get(collection_id, item_id);1396 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13971398 1399 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14001401 1402 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1403 .checked_sub(item.value as u64)1404 .unwrap();1405 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14061407 <FungibleItemList<T>>::remove(collection_id, item_id);14081409 Ok(())1410 }14111412 fn collection_exists(collection_id: u64) -> DispatchResult {1413 ensure!(1414 <Collection<T>>::contains_key(collection_id),1415 "This collection does not exist"1416 );1417 Ok(())1418 }14191420 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1421 Self::collection_exists(collection_id)?;14221423 let target_collection = <Collection<T>>::get(collection_id);1424 ensure!(1425 subject == target_collection.owner,1426 "You do not own this collection"1427 );14281429 Ok(())1430 }14311432 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1433 let target_collection = <Collection<T>>::get(collection_id);1434 let mut result: bool = subject == target_collection.owner;1435 let exists = <AdminList<T>>::contains_key(collection_id);14361437 if !result & exists {1438 if <AdminList<T>>::get(collection_id).contains(&subject) {1439 result = true1440 }1441 }14421443 result1444 }14451446 fn check_owner_or_admin_permissions(1447 collection_id: u64,1448 subject: T::AccountId,1449 ) -> DispatchResult {1450 Self::collection_exists(collection_id)?;1451 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());14521453 ensure!(1454 result,1455 "You do not have permissions to modify this collection"1456 );1457 Ok(())1458 }14591460 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1461 let target_collection = <Collection<T>>::get(collection_id);14621463 match target_collection.mode {1464 CollectionMode::NFT => {1465 <NftItemList<T>>::get(collection_id, item_id).owner == subject1466 }1467 CollectionMode::Fungible(_) => {1468 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1469 }1470 CollectionMode::ReFungible(_) => {1471 <ReFungibleItemList<T>>::get(collection_id, item_id)1472 .owner1473 .iter()1474 .any(|i| i.owner == subject)1475 }1476 CollectionMode::Invalid => false,1477 }1478 }14791480 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1481 let mes = "Address is not in white list";1482 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1483 let wl = <WhiteList<T>>::get(collection_id);1484 ensure!(wl.contains(address), mes);14851486 Ok(())1487 }14881489 fn transfer_fungible(1490 collection_id: u64,1491 item_id: u64,1492 value: u64,1493 owner: T::AccountId,1494 new_owner: T::AccountId,1495 ) -> DispatchResult {1496 ensure!(1497 <FungibleItemList<T>>::contains_key(collection_id, item_id),1498 "Item not exists"1499 );15001501 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1502 let amount = full_item.value;15031504 ensure!(amount >= value.into(), "Item balance not enouth");15051506 1507 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1508 .checked_sub(value)1509 .unwrap();1510 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15111512 let mut new_owner_account_id = 0;1513 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1514 if new_owner_items.len() > 0 {1515 new_owner_account_id = new_owner_items[0];1516 }15171518 let val64 = value.into();15191520 1521 if amount == val64 && new_owner_account_id == 0 {1522 1523 1524 let mut new_full_item = full_item.clone();1525 new_full_item.owner = new_owner.clone();1526 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15271528 1529 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1530 .checked_add(value)1531 .unwrap();1532 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15331534 1535 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1536 } else {1537 let mut new_full_item = full_item.clone();1538 new_full_item.value -= val64;15391540 1541 if new_owner_account_id > 0 {1542 1543 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1544 item.value += val64;15451546 1547 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1548 .checked_add(value)1549 .unwrap();1550 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15511552 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1553 } else {1554 1555 let item = FungibleItemType {1556 collection: collection_id,1557 owner: new_owner.clone(),1558 value: val64,1559 };15601561 Self::add_fungible_item(item)?;1562 }15631564 if amount == val64 {1565 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15661567 1568 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1569 <FungibleItemList<T>>::remove(collection_id, item_id);1570 }15711572 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1573 }15741575 Ok(())1576 }15771578 fn transfer_refungible(1579 collection_id: u64,1580 item_id: u64,1581 value: u64,1582 owner: T::AccountId,1583 new_owner: T::AccountId,1584 ) -> DispatchResult {1585 ensure!(1586 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1587 "Item not exists"1588 );15891590 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1591 let item = full_item1592 .owner1593 .iter()1594 .filter(|i| i.owner == owner)1595 .next()1596 .unwrap();1597 let amount = item.fraction;15981599 ensure!(amount >= value.into(), "Item balance not enouth");16001601 1602 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1603 .checked_sub(value)1604 .unwrap();1605 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16061607 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1608 .checked_add(value)1609 .unwrap();1610 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16111612 let old_owner = item.owner.clone();1613 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1614 let val64 = value.into();16151616 1617 if amount == val64 && !new_owner_has_account {1618 1619 1620 let mut new_full_item = full_item.clone();1621 new_full_item1622 .owner1623 .iter_mut()1624 .find(|i| i.owner == owner)1625 .unwrap()1626 .owner = new_owner.clone();1627 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16281629 1630 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1631 } else {1632 let mut new_full_item = full_item.clone();1633 new_full_item1634 .owner1635 .iter_mut()1636 .find(|i| i.owner == owner)1637 .unwrap()1638 .fraction -= val64;16391640 1641 if new_owner_has_account {1642 1643 new_full_item1644 .owner1645 .iter_mut()1646 .find(|i| i.owner == new_owner)1647 .unwrap()1648 .fraction += val64;1649 } else {1650 1651 new_full_item.owner.push(Ownership {1652 owner: new_owner.clone(),1653 fraction: val64,1654 });1655 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1656 }16571658 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1659 }16601661 Ok(())1662 }16631664 fn transfer_nft(1665 collection_id: u64,1666 item_id: u64,1667 sender: T::AccountId,1668 new_owner: T::AccountId,1669 ) -> DispatchResult {1670 ensure!(1671 <NftItemList<T>>::contains_key(collection_id, item_id),1672 "Item not exists"1673 );16741675 let mut item = <NftItemList<T>>::get(collection_id, item_id);16761677 ensure!(1678 sender == item.owner,1679 "sender parameter and item owner must be equal"1680 );16811682 1683 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1684 .checked_sub(1)1685 .unwrap();1686 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16871688 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1689 .checked_add(1)1690 .unwrap();1691 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16921693 1694 let old_owner = item.owner.clone();1695 item.owner = new_owner.clone();1696 <NftItemList<T>>::insert(collection_id, item_id, item);16971698 1699 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17001701 1702 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1703 Ok(())1704 }1705 1706 fn item_exists(1707 collection_id: u64,1708 item_id: u64,1709 mode: &CollectionMode1710 ) -> DispatchResult {1711 match mode {1712 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1713 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1714 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1715 _ => ()1716 };1717 1718 Ok(())1719 }17201721 fn set_re_fungible_variable_data(1722 collection_id: u64,1723 item_id: u64,1724 data: Vec<u8>1725 ) -> DispatchResult {1726 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17271728 item.variable_data = data;17291730 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17311732 Ok(())1733 }17341735 fn set_nft_variable_data(1736 collection_id: u64,1737 item_id: u64,1738 data: Vec<u8>1739 ) -> DispatchResult {1740 let mut item = <NftItemList<T>>::get(collection_id, item_id);1741 1742 item.variable_data = data;17431744 <NftItemList<T>>::insert(collection_id, item_id, item);1745 1746 Ok(())1747 }17481749 fn init_collection(item: &CollectionType<T::AccountId>) {1750 1751 assert!(1752 item.decimal_points <= 4,1753 "decimal_points parameter must be lower than 4"1754 );1755 assert!(1756 item.name.len() <= 64,1757 "Collection name can not be longer than 63 char"1758 );1759 assert!(1760 item.name.len() <= 256,1761 "Collection description can not be longer than 255 char"1762 );1763 assert!(1764 item.token_prefix.len() <= 16,1765 "Token prefix can not be longer than 15 char"1766 );17671768 1769 let next_id = CreatedCollectionCount::get()1770 .checked_add(1)1771 .expect("collection id error");17721773 CreatedCollectionCount::put(next_id);1774 }17751776 fn init_nft_token(item: &NftItemType<T::AccountId>) {1777 let current_index = <ItemListIndex>::get(item.collection)1778 .checked_add(1)1779 .expect("Item list index id error");17801781 let item_owner = item.owner.clone();1782 let collection_id = item.collection.clone();1783 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();17841785 <ItemListIndex>::insert(collection_id, current_index);17861787 1788 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1789 .checked_add(1)1790 .unwrap();1791 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1792 }17931794 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1795 let current_index = <ItemListIndex>::get(item.collection)1796 .checked_add(1)1797 .expect("Item list index id error");1798 let owner = item.owner.clone();1799 let value = item.value as u64;18001801 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18021803 <ItemListIndex>::insert(item.collection, current_index);18041805 1806 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1807 .checked_add(value)1808 .unwrap();1809 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1810 }18111812 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1813 let current_index = <ItemListIndex>::get(item.collection)1814 .checked_add(1)1815 .expect("Item list index id error");18161817 let value = item.owner.first().unwrap().fraction as u64;1818 let owner = item.owner.first().unwrap().owner.clone();18191820 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18211822 <ItemListIndex>::insert(item.collection, current_index);18231824 1825 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1826 .checked_add(value)1827 .unwrap();1828 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1829 }18301831 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18321833 1834 if <AccountItemCount<T>>::contains_key(owner.clone()) {18351836 1837 let count = <AccountItemCount<T>>::get(owner.clone());1838 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18391840 <AccountItemCount<T>>::insert(owner.clone(), 1841 count.checked_add(1).unwrap());1842 }1843 else {1844 <AccountItemCount<T>>::insert(owner.clone(), 1);1845 }18461847 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1848 if list_exists {1849 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1850 let item_contains = list.contains(&item_index.clone());18511852 if !item_contains {1853 list.push(item_index.clone());1854 }18551856 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1857 } else {1858 let mut itm = Vec::new();1859 itm.push(item_index.clone());1860 <AddressTokens<T>>::insert(collection_id, owner, itm);1861 1862 }18631864 Ok(())1865 }18661867 fn remove_token_index(1868 collection_id: u64,1869 item_index: u64,1870 owner: T::AccountId,1871 ) -> DispatchResult {18721873 1874 <AccountItemCount<T>>::insert(owner.clone(), 1875 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());187618771878 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1879 if list_exists {1880 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1881 let item_contains = list.contains(&item_index.clone());18821883 if item_contains {1884 list.retain(|&item| item != item_index);1885 <AddressTokens<T>>::insert(collection_id, owner, list);1886 }1887 }18881889 Ok(())1890 }18911892 fn move_token_index(1893 collection_id: u64,1894 item_index: u64,1895 old_owner: T::AccountId,1896 new_owner: T::AccountId,1897 ) -> DispatchResult {1898 Self::remove_token_index(collection_id, item_index, old_owner)?;1899 Self::add_token_index(collection_id, item_index, new_owner)?;19001901 Ok(())1902 }1903}1904190519061907190819091910pub type Multiplier = FixedU128;19111912type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1913 <T as system::Trait>::AccountId,1914>>::Balance;1915type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1916 <T as system::Trait>::AccountId,1917>>::NegativeImbalance;1918191919201921#[derive(Encode, Decode, Clone, Eq, PartialEq)]1922pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1923 #[codec(compact)] BalanceOf<T>,1924);19251926impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1927 for ChargeTransactionPayment<T>1928{1929 #[cfg(feature = "std")]1930 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1931 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1932 }1933 #[cfg(not(feature = "std"))]1934 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1935 Ok(())1936 }1937}19381939impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1940where1941 T::Call:1942 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1943 BalanceOf<T>: Send + Sync + FixedPointOperand,1944{1945 1946 pub fn from(fee: BalanceOf<T>) -> Self {1947 Self(fee)1948 }19491950 pub fn traditional_fee(1951 len: usize,1952 info: &DispatchInfoOf<T::Call>,1953 tip: BalanceOf<T>,1954 ) -> BalanceOf<T>1955 where1956 T::Call: Dispatchable<Info = DispatchInfo>,1957 {1958 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1959 }19601961 fn withdraw_fee(1962 &self,1963 who: &T::AccountId,1964 call: &T::Call,1965 info: &DispatchInfoOf<T::Call>,1966 len: usize,1967 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1968 let tip = self.0;19691970 1971 1972 let fee = match call.is_sub_type() {1973 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1974 _ => Self::traditional_fee(len, info, tip), 1975 1976 };19771978 1979 1980 let sponsor: T::AccountId = match call.is_sub_type() {1981 Some(Call::create_item(collection_id, _properties, _owner)) => {1982 <Collection<T>>::get(collection_id).sponsor1983 }1984 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1985 let _collection_mode = <Collection<T>>::get(collection_id).mode;19861987 1988 let sponsor_transfer = match _collection_mode {1989 CollectionMode::NFT => {1990 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1991 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1992 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1993 if block_number >= limit_time {1994 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1995 true1996 }1997 else {1998 false1999 }2000 }2001 CollectionMode::Fungible(_) => {2002 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2003 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2004 if basket.iter().any(|i| i.address == _new_owner.clone())2005 {2006 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2007 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2008 if block_number >= limit_time {2009 basket.retain(|x| x.address == item.address);2010 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2011 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2012 true2013 }2014 else {2015 false2016 }2017 }2018 else {2019 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2020 true2021 }2022 }2023 CollectionMode::ReFungible(_) => {2024 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2025 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2026 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2027 if block_number >= limit_time {2028 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2029 true2030 } else {2031 false2032 }2033 }2034 _ => {2035 false2036 },2037 };20382039 if !sponsor_transfer {2040 T::AccountId::default()2041 } else {2042 <Collection<T>>::get(collection_id).sponsor2043 }2044 }20452046 _ => T::AccountId::default(),2047 };20482049 let mut who_pays_fee: T::AccountId = sponsor.clone();2050 if sponsor == T::AccountId::default() {2051 who_pays_fee = who.clone();2052 }20532054 2055 if fee.is_zero() {2056 return Ok((fee, None));2057 }20582059 match <T as transaction_payment::Trait>::Currency::withdraw(2060 &who_pays_fee,2061 fee,2062 if tip.is_zero() {2063 WithdrawReason::TransactionPayment.into()2064 } else {2065 WithdrawReason::TransactionPayment | WithdrawReason::Tip2066 },2067 ExistenceRequirement::KeepAlive,2068 ) {2069 Ok(imbalance) => Ok((fee, Some(imbalance))),2070 Err(_) => Err(InvalidTransaction::Payment.into()),2071 }2072 }2073}20742075impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension2076 for ChargeTransactionPayment<T>2077where2078 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2079 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,2080{2081 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2082 type AccountId = T::AccountId;2083 type Call = T::Call;2084 type AdditionalSigned = ();2085 type Pre = (2086 BalanceOf<T>,2087 Self::AccountId,2088 Option<NegativeImbalanceOf<T>>,2089 BalanceOf<T>,2090 );2091 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2092 Ok(())2093 }20942095 fn validate(2096 &self,2097 who: &Self::AccountId,2098 call: &Self::Call,2099 info: &DispatchInfoOf<Self::Call>,2100 len: usize,2101 ) -> TransactionValidity {2102 let (fee, _) = self.withdraw_fee(who, call, info, len)?;21032104 let mut r = ValidTransaction::default();2105 2106 2107 r.priority = fee.saturated_into::<TransactionPriority>();2108 Ok(r)2109 }21102111 fn pre_dispatch(2112 self,2113 who: &Self::AccountId,2114 call: &Self::Call,2115 info: &DispatchInfoOf<Self::Call>,2116 len: usize,2117 ) -> Result<Self::Pre, TransactionValidityError> {2118 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2119 Ok((self.0, who.clone(), imbalance, fee))2120 }21212122 fn post_dispatch(2123 pre: Self::Pre,2124 info: &DispatchInfoOf<Self::Call>,2125 post_info: &PostDispatchInfoOf<Self::Call>,2126 len: usize,2127 _result: &DispatchResult,2128 ) -> Result<(), TransactionValidityError> {2129 let (tip, who, imbalance, fee) = pre;2130 if let Some(payed) = imbalance {2131 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2132 len as u32, info, post_info, tip,2133 );2134 let refund = fee.saturating_sub(actual_fee);2135 let actual_payment =2136 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2137 &who, refund,2138 ) {2139 Ok(refund_imbalance) => {2140 2141 2142 match payed.offset(refund_imbalance) {2143 Ok(actual_payment) => actual_payment,2144 Err(_) => return Err(InvalidTransaction::Payment.into()),2145 }2146 }2147 2148 2149 Err(_) => payed,2150 };2151 let imbalances = actual_payment.split(tip);2152 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2153 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2154 );2155 }2156 Ok(())2157 }2158}215921602161